SOLID и агрегатор событий

Я стараюсь следовать SOLID, насколько это возможно, и столкнулся со следующей проблемой при работе в приложении Prism:

У меня есть класс, который обрабатывает все события, которые модуль получает от других модулей. Поскольку у класса есть две обязанности (регистрация обработчиков и обработка событий), я решил разделить класс на две части, что приводит к тому, что я делаю обработчики событий общедоступными (что в любом случае кажется странным).

Что вы предпочитаете в этой ситуации: иметь две обязанности в одном классе или иметь общедоступные обработчики событий (или что-то еще, что мне не хватает)?

С Уважением.

ИЗМЕНИТЬ:

public class Handler
{
    public void MethodHandlingAggregatedEvent()
    {
    }
}

public class Register
{
    .... 

    public void RegisterHandler()
    {
        this.eventAggregator.GetEvent<XXX>().Subscribe(this.handler.MethodHandlingAggregatedEvent);
    }
}

person Ignacio Soler Garcia    schedule 28.04.2014    source источник
comment
Я не понимаю, почему вам нужно сделать обработчики событий общедоступными. Может быть, вы можете предоставить какой-нибудь псевдокод?   -  person jnovo    schedule 28.04.2014
comment
@jnovo: если вы регистрируете событие вне класса, обрабатывающего событие, вам нужно создать делегата этого метода вне класса, поэтому метод должен быть общедоступным. Добавлен код для представления проблемы.   -  person Ignacio Soler Garcia    schedule 28.04.2014


Ответы (2)


Чтобы лучше объяснить мой ответ, рассмотрим следующий пример:

  • Приложение Prism содержит ModuleX и ModuleY
  • EventA и EventB определены где-то, где оба модуля могут получить к ним доступ.
  • ModuleX содержит обработчики обоих событий.
  • ModuleY запускает эти события.

Также рассмотрите следующие аспекты SOLID (из Википедии)

Принцип единой ответственности: класс должен иметь только одну ответственность (т. е. только одно потенциальное изменение в спецификации программного обеспечения должно иметь возможность повлиять на спецификацию класса).

Принцип разделения интерфейсов: «множество клиентских интерфейсов лучше, чем один интерфейс общего назначения».[4]

Требования к функциональности, которую вы описываете при сопоставлении с этим примером, будут следующими:

  1. Зарегистрировать обработчик для события A
  2. Зарегистрировать обработчик для события B
  3. Обработать событие А
  4. Обработать событие B

Если у вас есть один класс, который обрабатывает как A, так и B, вы нарушаете принцип единой ответственности. Если вы зарегистрируете обработчик событий с обработчиком, определенным в том же классе, вы также нарушите принцип единой ответственности.

И что теперь? Если все разделить, получится четыре класса, поскольку это, по-видимому, разные обязанности. Согласно Википедии принцип SOLID существует для упрощения расширения и изменения существующего кода; поэтому он должен быть там, чтобы сделать вашу жизнь проще, а не сложнее.

Если мы создадим четыре класса, мы будем вынуждены открыть код обработчика событий для области вне класса, в котором он был создан. По моему скромному мнению, было бы лучше сохранить код обработки событий, а также регистрацию в наименьший возможный объем:

class MyHandler
{
    public MyHandler(IEventAggregator eventAggregator)
    {
        eventAggregator.GetEvent<EventA>().Register(HandleEventA);
    }

    private void HandleEventA(EventArgs args)
    {

    }
}

MyHandler теперь отвечает за обработку EventA. Несмотря на то, что он также регистрирует обработчик; можно по-прежнему рассматривать это как часть обработчика событий.

person Bas    schedule 28.04.2014
comment
Я понимаю вашу точку зрения, но вы знаете, жизнь проще, когда вы не делаете исключений, иначе каждый раз, когда вы применяете правило, вы должны учитывать, можно ли в данном случае применять правило или нет ... в любом случае, я понимаю то, что вы значит. Обычно я воспринимаю SOLID как свою личную библию :) Спасибо. - person Ignacio Soler Garcia; 28.04.2014

Я думаю, что ваш дизайн в основном в порядке.

  1. Ваш класс Register отвечает за сопоставление обработчиков событий.
  2. Каждый обработчик отвечает за обработку конкретных событий.

Я бы рекомендовал переименовать метод RegisterHandler() в RegisterHandlers(), чтобы отразить его поведение. Также сделайте все классы, реализующие интерфейсы. Таким образом, вы можете издеваться над ними в модульном тесте следующим образом:

_registerMock.Verify(x => x.RegisterHandlers(), Times.Once);

_eventAggregator.Invoke(SomeEvent);
_someHandler.Verify(x => x.MethodHandlingAggregatedEvent(), Times.Once);
person Dzianis Yafimau    schedule 18.04.2016