Как спроектировать классы для простого файлообменника на Python

«Как спроектировать классы для простого файлообменника на Python» — вопрос из категории ООП, который задают на 10% собеседований Python Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Основной принцип при проектировании — разделение ответственности (Separation of Concerns). Каждый класс должен выполнять одну четкую задачу. Для файлообменника можно выделить три ключевые сущности:

  1. File: Модель данных, представляющая файл. Хранит метаданные и содержимое.
  2. User: Представляет пользователя системы, который может выполнять действия (загружать, скачивать).
  3. Storage: Сервисный класс, отвечающий за логику хранения, добавления и поиска файлов.

Пример реализации:

# 1. Модель данных
class File:
    def __init__(self, name: str, size: int, content: bytes):
        self.name = name
        self.size = size
        self.content = content

    def get_info(self) -> str:
        return f"File: {self.name}, Size: {self.size} bytes"

# 2. Хранилище
class Storage:
    def __init__(self):
        # В реальном приложении здесь была бы логика работы с БД или файловой системой
        self._files = {}

    def add_file(self, file: File):
        if file.name in self._files:
            raise ValueError(f"File with name {file.name} already exists.")
        self._files[file.name] = file
        print(f"File '{file.name}' added to storage.")

    def get_file(self, name: str) -> File | None:
        return self._files.get(name)

# 3. Пользователь
class User:
    def __init__(self, username: str):
        self.username = username

    def upload_file(self, file: File, storage: Storage):
        print(f"User '{self.username}' is uploading '{file.name}'...")
        storage.add_file(file)

### Взаимодействие классов:

# Создаем экземпляры
storage = Storage()
user = User("alice")
file_to_upload = File("document.txt", 1024, b"some binary data")

# Пользователь загружает файл в хранилище
user.upload_file(file_to_upload, storage)

# Другой пользователь или система может получить файл из хранилища
retrieved_file = storage.get_file("document.txt")
if retrieved_file:
    print(f"Successfully retrieved: {retrieved_file.get_info()}")

Почему такой подход эффективен:

  • Инкапсуляция: Логика хранения файлов скрыта внутри класса Storage.
  • Масштабируемость: Легко добавить новые функции, например, класс Permissions для управления доступом, не изменяя существующие классы.
  • Тестируемость: Каждый класс можно тестировать независимо друг от друга.