Проектирование пользовательского интерфейса для отображения длительных бизнес-операций в рамках Чистой архитектуры требует особого подхода, чтобы следовать принципам разделения ответственности и поддерживать возможность тестирования и модификации. Ниже приведен подход, который может помочь вам реализовать такое решение:
### Структура архитектуры
1. **Слой ввода (Presentation Layer)**:
- **Контроллер (Controller)**: отвечает за обработку пользовательского ввода и взаимодействие с представлением. Он будет получать события из представления (например, нажатие кнопки "Начать обработку") и инициировать вызов соответствующего Use Case.
- **Презентер (Presenter)**: принимает данные от Use Case (через OutputPort) и преобразует их в формат, понятный для отображения в UI.
2. **Слой бизнес-логики (Domain Layer)**:
- **Use Case (Interactor)**: выполняет бизнес-логику и управляет состоянием операции. Он будет обращаться к описание проблемы и передавать обновления в Презентер.
- **OutputPort**: интерфейс, через который Use Case сообщает о состоянии выполнения и изменениях данных.
3. **Слой данных (Data Layer)**:
- Отвечает за взаимодействие с внешними системами/данными (например, записи в базу данных или обращения к файловой системе).
### Процесс выполнения длительной операции
1. **Инициация операции**:
- Контроллер обрабатывает событие начала обработки (например, нажатие кнопки) и вызывает метод в Use Case Interactor для начала длительной операции.
2. **Взаимодействие Use Case и OutputPort**:
- Внутри Use Case по мере выполнения длительной операции (например, обработки файлов и папок) будут происходить разные этапы, и в каждом из них Use Case будет вызывать методы OutputPort для передачи состояния (например, начало обработки новой папки, текущий обрабатываемый файл, результаты).
3. **Обновление UI через Презентер**:
- Презентер будет получать данные от OutputPort, преобразовывать их в структуру данных для отображения в UI и обновлять представление в пользовательском интерфейсе.
4. **Побочные эффекты и события**:
- Чтобы обеспечить «реактивность» интерфейса, вы можете использовать паттерн публикации/подписки. Презентер будет подписываться на изменения состояния и обновлять UI по мере поступления данных от Use Case.
### Пример реализации
```python
# OutputPort интерфейс
class OutputPort:
def on_folder_started(self, folder_name: str):
pass
def on_file_processing(self, file_name: str):
pass
def on_folder_processed(self, folder_name: str, result: str):
pass
# Use Case
class FileProcessingUseCase:
def __init__(self, output_port: OutputPort):
self.output_port = output_port
def process_files(self, folder_path: str):
# Обрабатываем папку
self.output_port.on_folder_started(folder_path)
# Имитация обработки файлов
for file in os.listdir(folder_path):
self.output_port.on_file_processing(file)
time.sleep(1) # Имитация длительной обработки
result = "Успех" # Результат обработки файла
self.output_port.on_folder_processed(folder_path, result)
# Презентер
class FileProcessingPresenter(OutputPort):
def update_ui(self):
# Логика обновления UI
pass
def on_folder_started(self, folder_name: str):
# Добавляем строку с именем папки в таблицу
self.update_ui()
def on_file_processing(self, file_name: str):
# Обновляем состояние обработки файла
self.update_ui()
def on_folder_processed(self, folder_name: str, result: str):
# Записываем результат в таблицу
self.update_ui()
# Контроллер
class FileProcessingController:
def __init__(self, use_case: FileProcessingUseCase):
self.use_case = use_case
def start_processing(self, folder_path: str):
self.use_case.process_files(folder_path)
# Пример использования
presenter = FileProcessingPresenter()
use_case = FileProcessingUseCase(presenter)
controller = FileProcessingController(use_case)
# Запуск обработки
controller.start_processing("/path/to/folder")
```
### Заключение
Такой подход позволяет вам разбить логику на четкие компоненты, следовать принципам Чистой архитектуры и гарантировать, что пользовательский интерфейс будет обновляться