#php #symfony #decoupling
#php #symfony #развязка
Вопрос:
Примечание: Я довольно часто ссылаюсь на консольный компонент Symfony в своем вопросе, но я думаю, что этот вопрос можно рассматривать шире, если рассматривать его в контексте любого пользовательского интерфейса.
Я использую Symfony Console component
для создания консольного приложения. Я пытаюсь свести связывание классов к минимуму, поскольку это упрощает модульное тестирование и, как правило, является лучшей практикой.
В моем приложении есть некоторые процессы, выполнение которых может занять некоторое время, поэтому я хочу информировать пользователя о том, что происходит, используя индикаторы выполнения и общий текстовый вывод по мере его выполнения. Symfony требует, чтобы экземпляр Symfony
консольного OutputInterface передавался в метод выполнения любой команды. Пока все хорошо; Я могу создавать индикаторы выполнения и выводить текст по своему усмотрению. Однако вся тяжелая работа моего приложения происходит не в методах выполнения команд, а в основных классах моего приложения. Эти классы не должны и не знают, что они используются в консольном приложении.
Я изо всех сил пытаюсь сохранить это таким образом, потому что я не знаю, как обеспечить обратную связь с консолью (или любым другим пользовательским интерфейсом), не вводя класс вывода в мое ядро. Это приведет к тесной связи между классом вывода консоли и ядром моего приложения. Я думал об использовании диспетчера событий (у Symfony есть один), но это также означает, что мое ядро будет связано с диспетчером (возможно, это нормально). В идеале мне нужно как бы «переместить» состояние моего приложения обратно в метод выполнения вызываемой команды, где я затем могу выполнить вывод.
Может кто-нибудь указать мне правильное направление, пожалуйста? Я чувствую, что это должно быть довольно распространенным случаем, но не могу найти много об этом.
Заранее спасибо за ваше время!
Ответ №1:
Я успешно использовал подход диспетчера событий раньше. Например, вы можете запускать события в начале, ходе и в конце обработки, и на основе этого прослушиватель событий обновляет индикатор выполнения.
<?php
$progress = $this->getHelperSet()->get('progress');
$dispatcher = $this->getContainer()->get('event_dispatcher');
$dispatcher->addListener('my_start_event', function (GenericEvent $event) use ($progress, $output) {
$progress->start($output, $event->getArgument('total'));
});
$dispatcher->addListener('my_progress_event', function () use ($progress) {
$progress->advance();
});
$dispatcher->addListener('my_finish_event', function () use ($progress) {
$progress->finish();
});
Если вы действительно хотите избежать связывания диспетчера событий в вашей службе, вы можете расширить или украсить свой класс (возможно, реализуя общий интерфейс) и использовать только диспетчер событий там. Однако вам потребуется точка расширения (общедоступный или защищенный метод) в базовом классе, чтобы иметь возможность уведомлять о любом прогрессе.
Комментарии:
1. Это звучит как самое простое решение. Я думаю, по крайней мере, тогда мое ядро приложения не будет знать о пользовательском интерфейсе и будет дополнительно заниматься запуском событий выполнения. Ах да, я понимаю, что вы имеете в виду, говоря о расширении моего сервиса (например, EventAwareService или что-то в этом роде) и наличии защищенного метода в исходном сервисе.