Проблема.
Пусть у нас есть WinForms приложение, созданное с применением шаблона Model-View-Presenter. Логичным шагом будет наличие главного презентера, реализующим логику работу главной формы. И одна из главных обязанностей - создание и отображение дочерних презентеров. Звучит просто, но в реализации возникает главная сложность: как создавать дочерние презентеры? Я бы принял во внимание два возможных варианта действий. Оба из них предполагают использования шаблона Ioc/DI, и любого контейнера на ваш вкус.
Рассмотрим нашего подопытного:
Название как-бы намекает нам на абстрактный класс, но лучше пропустить этот намёк и сделать интерфейс:
Необобщённая версия:
Вариант 2: Диспетчер событий.
Второй способ я уже описывал в прошлом посте. Но для ясности повторю идею. Если у нас есть Event Aggregator:
AbstractFactory vs EventAggregator.
Что же выбрать? Этот вопрос традиционно переходит в вопрос гибкость против сложности.
Абстрактная фабрика проста в реализации, ее легко отлаживать. В то же время сокрытие информации с диспетчером событий даёт фантастическую гибкость. Я предпочитаю гибкость диспетчера событий, т.к. набрать немного простого кода не так сложно, а поддерживать его намного проще.
Стоит отметить, что грамотное использование современных контейнеров (и точек их расширения) позволяет существенно уменьшить объёмы ручного труда в обоих вариантах. Но об этом позже.
Пусть у нас есть WinForms приложение, созданное с применением шаблона Model-View-Presenter. Логичным шагом будет наличие главного презентера, реализующим логику работу главной формы. И одна из главных обязанностей - создание и отображение дочерних презентеров. Звучит просто, но в реализации возникает главная сложность: как создавать дочерние презентеры? Я бы принял во внимание два возможных варианта действий. Оба из них предполагают использования шаблона Ioc/DI, и любого контейнера на ваш вкус.
Рассмотрим нашего подопытного:
public class MainPresenter { public void ShowClients() { //как создать и отобразить презентер? } public void ShowVendors() { //как создать и отобразить презентер? } // Много других аналогичных методов. }Вариант 1: Абстрактная фабрика.
Название как-бы намекает нам на абстрактный класс, но лучше пропустить этот намёк и сделать интерфейс:
public interface IPresenterFactory { IPresenter CreateClientsPresenter(); IPresenter CreateVendorsPresenter(); // Создание остальных видов презентеров }И тогда код презентера обретает вид:
public class MainPresenter { public MainPresenter(IPresenterFactory presenterFactory) { _presenterFactory = presenterFactory; } public void ShowClients() { var presenter = _presenterFactory.CreateClientsPresenter(); presenter.Start(); } public void ShowVendors() { var presenter = _presenterFactory.CreateVendorsPresenter(); presenter.Start(); } // Много других аналогичных методов. }Еще одна вариация на тему - обобщённая версия абстрактной фабрики:
public interface IPresenterFactory { IPresenter CreatePresenter<TPresenter>() where TPresenter : IPresenter; }Замечу, возвращаемое значение IPresenter. Возвращать TPresenter крайне нежелательно, поскольку мы сильно затрудним тестирование. У конкретных реализаций презентеров могут быть зависимости, у которых в свою очередь могут быть зависимости... Создать модульный тест в таком случае будет почти невозможно. Для обобщённой фабрики презентер будет выглядеть так:
public class MainPresenter { public MainPresenter(IPresenterFactory presenterFactory) { _presenterFactory = presenterFactory; } public void ShowClients() { var presenter = _presenterFactory.Create<ClientsPresenter>(); presenter.Start(); } public void ShowVendors() { var presenter = _presenterFactory.Create<VendorsPresenter>(); presenter.Start(); } // Много других аналогичных методов. }Теперь окинем код взглядом еще раз, и оценим достоинства.
Необобщённая версия:
- + Конкретная реализации презентеров скрыта.
- + Возможные варианты создаваемых презентеров явно видны.
- + В создающие методы можно добавить параметры.
- - Много однообразного кода.
- + "Халявная" поддежка новых типов презентеров.
- + Экономия кода.
- - Нарушение принципов сокрытия информации и OCP.
Вариант 2: Диспетчер событий.
Второй способ я уже описывал в прошлом посте. Но для ясности повторю идею. Если у нас есть Event Aggregator:
public interface IPublisher { void Publish<TMessage>(TMessage message); }Чтобы использовать диспетчер событий, надо создать классы событий:
public class ShowClientsMessage{} public class ShowVendorsMessage{}И классы обработчики событий:
public class ShowClientsMessageListener: IListener<ShowClientsMessageListener> { public ShowClientsMessageListener(ClientsPresenter presenter) { _presenter = presenter; } public void ListenTo(ShowClientsMessage message) { presenter.Start(); } } // второй класс не показан для краткости.То с их помощью можно значительно упростить главный презетер:
public class MainPresenter { public MainPresenter(IPublisher publisher) { _publisher = publisher; } public void ShowClients() { _publisher.Publish(new ShowClientsMessage()); } public void ShowVendors() { _publisher.Publish(new ShowVendorsMessage()); } // Много других аналогичных методов. }Детали реализации можно найти в предыдущих постах. А сейчас немного о преимуществах и недостатках:
- + Скрыта не только конкретная реализация презентеров, но способы их создания и отображения! Это даёт огромную гибкостью
- + Через классы сообщений легко передавать параметры.
- + Высокая тестируемость главного презентера и обработчиков событий.
- - Требуется больше намного больше кода.
- - Менее очевидны зависимости
AbstractFactory vs EventAggregator.
Что же выбрать? Этот вопрос традиционно переходит в вопрос гибкость против сложности.
Абстрактная фабрика проста в реализации, ее легко отлаживать. В то же время сокрытие информации с диспетчером событий даёт фантастическую гибкость. Я предпочитаю гибкость диспетчера событий, т.к. набрать немного простого кода не так сложно, а поддерживать его намного проще.
Стоит отметить, что грамотное использование современных контейнеров (и точек их расширения) позволяет существенно уменьшить объёмы ручного труда в обоих вариантах. Но об этом позже.
Комментариев нет:
Отправить комментарий