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

EventAggregator можно найти во многих WPF-каркасах: Mvvm Light -класс Messenger, Catel – класс MessageMediator. Я познакомился с EventAggregator вместе с WPF каркасом Prism. Использование EventAggregator оказалось простым и гибким. Компоненты системы становятся независимыми друг от друга – изменяя один компонент, я не боюсь сломать другой.

При рассмотрении отдельных компонент все так и есть, но поднявшись на уровень работы компонентов в системе, можно разглядеть серьёзные проблемы:


Делюсь моим взглядом на слишком слабую связанность и не явное взаимодействие между частями системы.

Управление светодиодами через EventAggregator


Для управления светодиодами понадобятся: кнопка питания – Power, переключатель с двумя состояниями – Switch и два светодиода – RedLed и BlueLed. На WPF это выглядит, как то так:


Кнопка Power зажигает один из светодиодов в зависимости от состояния переключателя Switch.

В системе основанной на EventAggregator выделим два события: включения/выключения питания — PowerEvent и изменение состояния переключателя — SwitchEvent.

Событие PowerEvent публикуется при нажатии на кнопку Power, событие SwitchEvent публикуется при нажатии на Switch. Светодиоды подписываются на события PowerEvent и SwitchEvent.

Светодиод зажигается, если есть питание и переключатель находится в нужном состоянии.

Код событий
enum Power
    {
        On = 1,
        Off = 0,
    }

    class PowerEvent : PubSubEvent<Power>
    {
    }
    public enum SwitchConnection
    {
        Connection1,
        Connection2,
    }

    class SwitchEvent : PubSubEvent<SwitchConnection>
    {
    }


Код управления питанием
public class PowerViewModel : BindableBase
    {
        readonly IEventAggregator _aggregator;
        bool _power;

        public PowerViewModel(IEventAggregator aggregator)
        {
            _aggregator = aggregator;
        }

        public bool Power
        {
            get { return _power; }
            set
            {
                if (SetProperty(ref _power, value))
                    _aggregator.GetEvent<PowerEvent>().Publish(_power ? Events.Power.On : Events.Power.Off);
            }
        }
    }


Код управления переключателем
public class SwitchViewModel : BindableBase
    {
        readonly IEventAggregator _aggregator;

        bool _switch;

        public SwitchViewModel(IEventAggregator aggregator)
        {
            _aggregator = aggregator;
            Switch = true;
        }

        public bool Switch
        {
            get { return _switch; }
            set
            {
                if (SetProperty(ref _switch, value))
                    _aggregator.GetEvent<SwitchEvent>().Publish(_switch ? SwitchConnection.Connection1 : SwitchConnection.Connection2);
            }
        }

    }


Код светодиода
/// <summary>
    /// ViewModel светодиода.
    /// </summary>
    public class LedViewModel : BindableBase
    {
        readonly SwitchConnection _activeConnection;
        readonly Brush _activeLight;
        Power _currentPower;
        SwitchConnection _currentConnection;
        Brush _currentlight;

        public LedViewModel(SwitchConnection connection, Brush light, IEventAggregator aggregator)
        {
            _activeConnection = connection;
            _activeLight = light;

            aggregator.GetEvent<PowerEvent>().Subscribe(OnPowerChanged);
            aggregator.GetEvent<SwitchEvent>().Subscribe(OnSwitch);

            Update();
        }

        /// <summary>
        /// Свет от светодиода.
        /// </summary>
        public Brush Light
        {
            get { return _currentlight; }
            private set
            {
                SetProperty(ref _currentlight, value);
            }
        }

        /// <summary>
        /// Обработчик переключателя.
        /// </summary>
        void OnSwitch(SwitchConnection connection)
        {
            if (SetProperty(ref _currentConnection, connection))
                Update();
        }

        /// <summary>
        /// Обработчик питания.
        /// </summary>
        void OnPowerChanged(Power power)
        {
            if (SetProperty(ref _currentPower, power))
                Update();
        }

        void Update()
        {
            Brush currentLight = Brushes.Transparent;

            switch (_currentPower)
            {
                case Power.On:
                    if (_currentConnection == _activeConnection)
                        currentLight = _activeLight;
                    break;
                case Power.Off:
                    break;
                default:
                    throw new ArgumentOutOfRangeException();
            }

            Light = currentLight;
        }

    }


Xaml разметка
<Window x:Class="AggregatorAntiPattern.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:AggregatorAntiPattern"
        mc:Ignorable="d"
         Height="350" Width="525">
    <Window.Resources>
        <Style TargetType="Path" x:Key="Light">
            <Setter Property="Stroke" Value="Black" />
            <Setter Property="StrokeThickness" Value="2" />
            <Setter Property="Fill" Value="{Binding Light}" />
            <Setter Property="Data">
                <Setter.Value>
                    <EllipseGeometry RadiusX="10" RadiusY="10" />
                </Setter.Value>
            </Setter>
        </Style>
        <Style TargetType="Line" x:Key="Connection">
            <Setter Property="Stroke" Value="Black" />
            <Setter Property="StrokeThickness" Value="1" />
        </Style>
    </Window.Resources>
    <Canvas Margin="20">
        <ToggleButton Canvas.Top="120" Content=" Power " DataContext="{Binding PowerVM}" IsChecked="{Binding Power}" />
        <Line Canvas.Top="130" Canvas.Left="40" X1="0" X2="90" Y1="0" Y2="0" Style="{StaticResource Connection}" />
        <ToggleButton Canvas.Top="120" Canvas.Left="120" Content=" Switch " DataContext="{Binding SwitchVM}" IsChecked="{Binding Switch}" />
        <Line Canvas.Top="130" Canvas.Left="165" X1="0" X2="77" Y1="0" Y2="-30" Style="{StaticResource Connection}" />
        <Line Canvas.Top="130" Canvas.Left="165" X1="0" X2="77" Y1="0" Y2="30" Style="{StaticResource Connection}" />
        <Path Canvas.Top="100" Canvas.Left="250" DataContext="{Binding Connection1Light}" Style="{StaticResource Light}" />
        <Path Canvas.Top="160" Canvas.Left="250" DataContext="{Binding Connection2Light}" Style="{StaticResource Light}" />
    </Canvas>
</Window>


Связующий код
var aggregator = new EventAggregator();

            PowerVM = new PowerViewModel(aggregator);
            SwitchVM = new SwitchViewModel(aggregator);
            Connection1Light = new LedViewModel(SwitchConnection.Connection1, Brushes.Red, aggregator);
            Connection2Light = new LedViewModel(SwitchConnection.Connection2, Brushes.Blue, aggregator);


Все работает отлично!

Проблемы с EventAggregator


В схеме всего 4 компонента, но что нужно сделать, что бы светодиод заменить на другой элемент? Скопипастить подписку из LedViewModel – продублировать.

При динамической замене компонент, все еще хуже, везде нужно будет дублировать отписку. EventAggregator по умолчанию создает weakreference. С Weakreference отписка должна проходить автоматически, но при динамической замене компонент, неизвестно когда будет удалена подписка — везде нужно будет дублировать явную отписку.

Заменив компонент, я не знаю в каком состоянии система: включено ли питание, в каком положении Switch, мне просто не откуда это взять. Одно из решений – ввести в систему вспомогательное событие. Вспомогательное событие будет просить компоненты опубликовать свои события – PowerEvent и SwitchEvent. Теперь везде нужно позаботиться о публикации и подписке на это событие – система распадается и превращается в паутину.

Компоненты системы знают только об EventAggregator, но означает ли это слабую связанность? Нет. Несмотря на изолированность компонент друг от друга, в системе присутствует очень сильная неявная связь. Сильная связь выражена в наборе событий, которые нужно обрабатывать. Я не могу заменить Switch на другой компонент, не доработав Led. В результате связь между частями системы превращается в узел: сильная, не явная и запутанная.

Что нужно сделать, что бы в схеме было несколько Switch?


Прежде чем получить ответ, хорошо подумйте.


Про использование EventAggregator внутри сервисов, которые реализуют некоторый интерфейс и подменяются в зависимости от конфигурации… Лучше не вспоминать.

Откуда ростут проблемы


Использование EventAggregator нарушает 3 из 5 принципов SOLID. Единственность ответственности – подписка/отписка не забота компонентов схемы. Открытость закрытость – при изменении схемы взаимодействия компонентов, нужно править подписку/отписку. Инверсия зависимости – компонент сам решает, на какие события подписываться/отписываться.
3 из 5, а проблем…

P.S. используйте EventAggregator с осторожностью. Для меня EventAggregator – антипаттерн и бед от него намного больше чем пользы.
Поделиться с друзьями
-->

Комментарии (47)


  1. vintage
    27.02.2017 08:42
    +2

    Я не очень понял, что вы предлагаете использовать вместо этого паттерна.


    1. werwolfby
      27.02.2017 09:27
      +1

      Вот и мне тоже интересно. Я согласен с автором, но какая альтернатива? Утки? (С)


      1. vintage
        27.02.2017 09:40
        -1

        Хорошая альтернатива — реактивное программирование.


      1. InWake
        27.02.2017 17:08

        Связи должны быть с наружи компонента, а не внутри. Сам компонент не должен решать куда ему встроиться. как это сделать? Напишу статью.


  1. VolCh
    27.02.2017 09:38
    +4

    Использование EventAggregator нарушает 3 из 5 принципов SOLID.

    Как-то многовато насчитали. Может проблема не в паттерне, а в не очень удачном его применении?


    По SRP в коде модели светодиода не должно быть никаких подписок, только состояние "горит/не горит" и метод "переключить" (вариант "зажечь" и "погасить"). То же с кнопками — состояние и метод для его изменения. Отдельно собственно схема — модель, хранящая ссылки на модели кнопок и светодиодов, и реализующая основную логику методами "нажать кнопку повер", "нажать переключатель". И отдельно собственно подписчик на события интерфейса, преобразующий их в методы схемы.


    Можно реализовать на событийной модели и агрегаторе и собственно бизнес-логику, но обязательно должен быть преобразователь событий интерфейса в события модели.


    Вообще, если есть предположения, что приложение будет развиваться для моделирования произвольных схем (постоянного тока без учета переходных процессов для начала, для школы например), то я бы сделал "коммутатор", основная внешняя функция которого транслировать события "подано питание" с одного или нескольких входов на один или несколько выходов в зависимости от состояния "таблицы коммутации", плюс обрабатывать события изменения этой таблицы. Такими коммутаторами в схеме являются и кнопки, и светодиоды, и проводники их соединяющие, и даже отсутствующий источник питания. Проводники — вырожденный случай, транслирующие события с одного входа на другой без состояния, светодиоды (и любые подобные устройства) — то же самое, но имеющее побочный эффект, выключатель — почти тот же проводник, но имеющий состояние, указывающее генерировать ли событие на выходе при поступлении события на входе и метод для его переключения. Переключатель — почти тот же выключатель, но с состоянием, указывающим на какой из двух выходов транслировать событие со входа. Источник питания — коммутатор без входов и с одним выходом, который просто генерирует событие при старте моделирования. "Сборка" же схемы будет состоять из инстанцирования коммутаторов (трансляторов событий), инициализации их схем коммутации, привязки к некоторым из них исполнительных устройств, и, главное, подписки их на события друг друга через агрегатор согласно схеме, в идеале описанной декларативно, что позволит менять её бех кодирования, а читать из файлов, бд, или просто UI. Ответственность коммутаторов — транслировать события из агрегатора в агрегатор и, если с коммутатором связано (sic! композиция, а не наследование!) исполнительное устройство, дергать его соответствующий метод.


    1. InWake
      27.02.2017 17:20

      Схемы выбраны как абстракция. В схему можно заменить на систему фильтров — где Power и switch — критерии поиска, а Led'ы — результат фильтрации. Фантазировать можно как угодно.


      1. VolCh
        27.02.2017 18:11

        В любом случае, если модель предметной области не событийная, то классы объектов модели надо стараться отделять от служебной событийной инфраструктуры. Впрочем это любой инфраструктуры касается.


  1. lair
    27.02.2017 12:09
    +7

    Перед прочтением необходимо почитать о шаблоне EventAggregator

    Ну так привели бы определение шаблона, или хотя бы ссылку дали. А то ведь не понятно, что именно вы под ним понимаете.


    За отсутвием лучшего, возьмем фаулеровское определение: "An Event Aggregator acts as a single source of events for many objects. It registers for all the events of the many objects allowing clients to register with just the aggregator."


    Управление светодиодами через EventAggregator

    А кто вам сказал, что это подходящий шаблон для вашей задачи? Вернемся к Фаулеру: "Event Aggregator is a good choice when you have lots of objects that are potential event sources" (выделение мое). Это ваш кейс? Нет. У вас два семантически связанных, но при этом разнородных события.


    А теперь представьте, что у вас задача вида: "у меня есть k источников новостей и m устройств, отображающих новости в виде скроллера". Вы предлагаете каждое из устройств подписывать на каждый источник? Получите k*m связей (и в каждом устройстве нужно будет делать обнаружение источников). А можно взять аггрегатор, который будет сам подписываться на все источники, а все устройства подписывать только на аггрегатор: получается k+m связей, логика обнаружения источников только в агрегаторе, в устройствах тривиальная подписка.


    Собственно, если задуматься, половина модного нынче Rx — это как раз event aggregator.


    Использование EventAggregator нарушает 3 из 5 принципов SOLID.

    А нарушает ли? Давайте проверим.


    Единственность ответственности – подписка/отписка не забота компонентов схемы.

    А чья? В событийной модели кто-то должен отвечать за подписку/отписку. И этот кто-то, что характерно, не зависит от того, аггрегатор у вас, или просто обсервер. Так что пункт не засчитан.


    Открытость закрытость – при изменении схемы взаимодействия компонентов, нужно править подписку/отписку.

    Во-первых, нет. Подписка/отписка осталась той же самой — аггрегатор на все источники, все потребители на аггрегатор.


    Во-вторых, тот факт, что при изменении схемы взаимодействия надо править код, не является подтверждением нарушения OCP. Если у вас изменилась бизнес-логика, логично, что меняется код, ее реализующий.


    Пункт снова не засчитан.


    Инверсия зависимости – компонент сам решает, на какие события подписываться/отписываться.

    Иии что? До тех пор, пока компонент зависит от абстракции агрегатора, а не от конкретной реализации, DIP не нарушен.


    Так что из трех названных вами пунктов в реальности не нарушен ни один.


    1. InWake
      27.02.2017 18:02

      А чья? В событийной модели кто-то должен отвечать за подписку/отписку. И этот кто-то, что характерно, не зависит от того, аггрегатор у вас, или просто обсервер. Так что пункт не засчитан.

      Отличная система где все на все подписываются. В отличии от EventAggregator с его конкретным типом события, Observer взаимодействует с реализацией интерфейса. Реализация приходит в конструкторе или методе — наблюдатель не выбирает за чем наблюдать.
      Во-первых, нет. Подписка/отписка осталась той же самой — аггрегатор на все источники, все потребители на аггрегатор.
      Речь про добавление новых событий в системе. Вы использовали EventAggregator в своих проектах?

      Иии что? До тех пор, пока компонент зависит от абстракции агрегатора, а не от конкретной реализации, DIP не нарушен.
      ))) Зависимость не от аггрегатора, а от конкретных событий.

      lair мне кажется вы просто не сталкивались с EventAggregator. Это очень круто когда в системе 300 событий половина из которых дубляж, просто потому что событие с таким типом уже где то там используется и что бы ничего не сломать нужно создать то же самое событие но по другому назвать. А еще в системе присутствует не явная зависимость от последовательности подписки, и если ее нарушить все рухнет. Зафига там что то проектировать улучшать? Сделал новое событие, а потом еще и еще… EventAggregator способствует запутыванию кода.


      1. lair
        27.02.2017 18:09

        Отличная система где все на все подписываются.

        Чем она отличная? Тем, что количество связей растет с неимоверной скоростью?


        В отличии от EventAggregator с его конкретным типом события, Observer взаимодействует с реализацией интерфейса.

        А что мешает EA взаимодействовать с реализацией интерфейса?


        Реализация приходит в конструкторе или методе — наблюдатель не выбирает за чем наблюдать.

        ...EA — это всего лишь промежуточный обсервер. Вам никто не мешает сделать так же.


        Речь про добавление новых событий в системе.

        Добавили новые события — поправили тех, кто от этих событий зависит. Все как с обсервером.


        Вы использовали EventAggregator в своих проектах?

        Да, просто не в описанной вами реализации.


        Зависимость не от аггрегатора, а от конкретных событий.

        Ну так это особенность событийной модели, а не EA.


        Это очень круто когда в системе 300 событий половина из которых дубляж, просто потому что событие с таким типом уже где то там используется и что бы ничего не сломать нужно создать то же самое событие но по другому назвать.

        А может это проблема того, что у вас в системе зачем-то должны быть уникальные типы событий?


        А еще в системе присутствует не явная зависимость от последовательности подписки, и если ее нарушить все рухнет.

        Это уж точно не проблема EA.


        EventAggregator способствует запутыванию кода.

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


        1. InWake
          28.02.2017 07:10

          Чем она отличная? Тем, что количество связей растет с неимоверной скоростью?

          именно об этом я и пишу, количество событий начинает разрастаться.

          ...EA — это всего лишь промежуточный обсервер. Вам никто не мешает сделать так же.

          не мешает, но как обычно используют EventAggregator — проброс в конструктор и подписка на любые доступные события.

          А что мешает EA взаимодействовать с реализацией интерфейса?

          Практика использования — Подписывайся когда хочешь и на что хочешь.

          Добавили новые события — поправили тех, кто от этих событий зависит. Все как с обсервером.

          нет. Если объект сам решает на что ему подписываться.

          Да, просто не в описанной вами реализации.

          все персонажи вымышленные совпадения случайны.

          А может это проблема того, что у вас в системе зачем-то должны быть уникальные типы событий?

          Уникальные типы появляются, когда одни и те же данные имеют разное предназночение. Например строка может быть сообщением об ошибке, заголовком окна или еще чем то.

          Это уж точно не проблема EA.

          Даже у совершенного оружия есть недостаток — его владелец ©. Многие считают, что при использование EventAggregator не будет проблем, они ошибаются. Очень тяжело отслеживать события.

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

          А разве EventAggregator не основан на событийной модели? Название даже содержит Event…


          1. VolCh
            28.02.2017 07:18

            именно об этом я и пишу, количество событий начинает разрастаться.

            События не могут увеличивать количество связей, при росте количества связей в худшем случае количество событий растёт так же.


            Добавили новые события — поправили тех, кто от этих событий зависит. Все как с обсервером.
            нет. Если объект сам решает на что ему подписываться.

            Так решает и обсервер.


            Многие считают, что при использование EventAggregator не будет проблем, они ошибаются. Очень тяжело отслеживать события.

            Вы не путаете событийную модель вообще и один из её паттернов? Гораздо проще отслеживать события, если они все проходят через одной точку, чем когда у каждого события свой маршрут.


            А разве EventAggregator не основан на событийной модели? Название даже содержит Event…

            Вроде различаете, но как-то странно. EventAggregator предназначен для упрощения работы с событийной модели. Сначала принимается решение использовать событийную модель, а потом для её упрощения вводится EventAggregator.


            1. InWake
              28.02.2017 07:29

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

              Проходят через одну точку? Регистрируются да, но уж точно не проходят.


              1. VolCh
                28.02.2017 07:59

                В моей ментальной модели они именно проходят. Физическая реализация значения мало имеет.


                1. InWake
                  28.02.2017 09:54

                  Дело не в реализации. И как вы их будете отслеживать?


                  1. VolCh
                    28.02.2017 10:01

                    Подпишусь логгером на все события агрегатора и буду писать их в один лог. Или не на все, только на интересующие.


                    1. InWake
                      28.02.2017 10:24

                      Подпишусь логгером на все события агрегатора

                      При добавлении нового события нужно не забыть подписать лог? А что это даст кроме информации что событие произошло?


                      1. VolCh
                        28.02.2017 10:33

                        Не нужно, если есть возможность подписаться на базовый класс Event, наследниками которого являются все события.


                        1. InWake
                          03.03.2017 23:14

                          а какая польза будет от такого логирования?


                          1. VolCh
                            04.03.2017 13:36

                            Буду их отслеживать, потому что вам это нужно :)


            1. InWake
              28.02.2017 07:31

              Так решает и обсервер.

              В случае наблюдателя имеется ограниченный набор на что можно подписаться, с агрегацией событий это не так.


              1. VolCh
                28.02.2017 08:00

                Набор событий всегда ограниченный.


                1. InWake
                  28.02.2017 09:50
                  +1

                  кто его ограничивает? Как скрыть не нужные события? В наблюдателе все это ограничивается интерфейсом, если у интерфейса десятки методов для подписки, то чем это отличается от аггрегатора?


                  1. VolCh
                    28.02.2017 09:58

                    Совокупность всех эмиттеров. Если каждый генерит по 10 типов событий и их всего 100, то число событий ограничено 1000.


                    Не надо подписываться на ненужные события. А то и не не надо их генерировать :)


                    Агрегатор собирает однотипные события в единый поток, позволяя реакторам подписываться на этот тип событий только в одном месте, а не переписывать код каждого наблюдателя при добавлении нового эмиттера.


                    1. InWake
                      28.02.2017 10:29

                      Не надо подписываться на ненужные события. А то и не не надо их генерировать :)

                      С этим спорить сложно.

                      Агрегатор собирает однотипные события в единый поток, позволяя реакторам подписываться на этот тип событий только в одном месте, а не переписывать код каждого наблюдателя при добавлении нового эмиттера.

                      При добавлении нового эмитера нужно дорабатывать все реакторы которые ожидают новое событие.


                      1. VolCh
                        28.02.2017 10:36

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


              1. lair
                28.02.2017 11:22

                В случае наблюдателя имеется ограниченный набор на что можно подписаться, с агрегацией событий это не так.

                Вообще-то, паттерн EA никак не подразумевает "неограниченный набор событий". В аггрегате ровно те события, которые вы решили аггрегировать, никаких больше.


                1. InWake
                  03.03.2017 23:13

                  агрегация ни как не поддается контролю.


                  1. lair
                    04.03.2017 02:06
                    +1

                    Да ладно. Агрегатор А агрегирует события типа Х с критерием У, агрегатор Б агрегирует события типа Й с критерием У2, подписчик Ц подписывается на А, подписчик Д — тоже на А, а подписчике Е — на А и Б. Что тут "не поддается контролю"?


            1. InWake
              28.02.2017 07:33

              Сначала принимается решение использовать событийную модель, а потом для её упрощения вводится EventAggregator.

              И как он ее упростит?


              1. VolCh
                28.02.2017 08:05

                У каждого эмиттера событий в идеале только один подписчик — агрегатор, а каждый реактор на события подписывается только на один эмиттер — агрегатор. Количество связей эмиттер-реактор снижается в лучшем случае с N*M на N+M. Усложняет он схему распространения событий только в простейших случаях, когда только один эмиттер или только один реактор.


                1. InWake
                  28.02.2017 10:21

                  У каждого эмиттера событий в идеале только один подписчик — агрегатор, а каждый реактор на события подписывается только на один эмиттер — агрегатор.

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


                  1. VolCh
                    28.02.2017 10:37

                    Не возникнут, если они используют единый интерфейс (в широком смысле слова).


                    1. InWake
                      03.03.2017 23:08

                      О каком интерфейсе речь? Что хочу публикую, что хочу слушаю?

                      Вот у вас есть интерфейс сервиса. Если конкретная реализация этого сервмса внутри себя подписывается или публикует что либо, через аггрегатор. Что нужно сделать для замены реализации?


                      1. VolCh
                        04.03.2017 13:38

                        Публикую наследников от AbstractEvent, слушаю их же. Интерфейс публикации и подписки зафиксирован.


                        Заменить нужно старую логику подписок и публикаций на новую, не меняя интерфейса.


                  1. lair
                    28.02.2017 11:23

                    Если подписка будет происходить вне эмиттера и реактора то да

                    … реализация "подписки", заметим, не является зафиксированной частью паттерна EventAggregator. Так что ваши претензии снова не по адресу.


                    1. InWake
                      03.03.2017 23:03

                      Не является. Но часто, в том же Prism, EventAggregator резолвится через IoC в конструктор.
                      Хуже всего что все примеры и документация на это заточены. Да это очень просто сделать, а вот сопровождать тяжело.


                      1. lair
                        04.03.2017 02:05

                        Да это очень просто сделать, а вот сопровождать тяжело.

                        Кому как.


          1. lair
            28.02.2017 11:21

            именно об этом я и пишу, количество событий начинает разрастаться.

            События должны определяться задачей. Почему они у вас разрастаются?


            как обычно используют EventAggregator — проброс в конструктор и подписка на любые доступные события.
            Практика использования — Подписывайся когда хочешь и на что хочешь.

            Это проблема тех, кто его неправильно использует.


            Если объект сам решает на что ему подписываться.

            Это называется "инкапсуляция". Что в этом плохого?


            Уникальные типы появляются, когда одни и те же данные имеют разное предназночение. Например строка может быть сообщением об ошибке, заголовком окна или еще чем то.

            Во-первых, это не "одни и те же данные". А во-вторых, если мы не говорим про type-driven (а мы про него явно не говорим), то почему у вас тип данных и их предназначение связаны? Потому что у вас единственное, что определяет событие — это его тип?


            Многие считают, что при использование EventAggregator не будет проблем, они ошибаются.

            … но это не проблема EA. Это проблема тех, кто так считает. Недостатки EA — те же, что и у обсервера — явно упомянуты в описании паттерна.


            А разве EventAggregator не основан на событийной модели? Название даже содержит Event…

            Конечно, основан. У вас есть претензии к событийной модели? Понимаю, окей. Откажитесь от событийной модели вовсе.


            … и чем это отличается от вашего ""


    1. mbeloshitsky
      28.02.2017 06:28
      +1

      А теперь представьте, что у вас задача вида: «у меня есть k источников новостей и m устройств, отображающих новости в виде скроллера». Вы предлагаете каждое из устройств подписывать на каждый источник? Получите k*m связей (и в каждом устройстве нужно будет делать обнаружение источников). А можно взять аггрегатор, который будет сам подписываться на все источники, а все устройства подписывать только на аггрегатор: получается k+m связей, логика обнаружения источников только в агрегаторе, в устройствах тривиальная подписка.


      Ну это вы описали хороший кейс использования EA. У меня было больше плохих кейсов — вида «надоело разбираться с MEF'ом (или любым другим DI-контейнером) и взаимодействовать через интерфейсы, сделаем ка мы штуку, которая броадкастит события, которые мы потом будем в ручную диспетчеризовать в получателях по строковым тегам». По первости это все дело работает, но по мере его внедрения и увеличения количества передаваемых типов сообщений начинает разваливаться. Компилятор всю эту систему не проверяет и она начинает жить своей жизнью. Возможно именно эти случаи имел ввиду автор.


      1. InWake
        28.02.2017 07:15

        Да. Обратная сторона одного события. Всю информацию засунуть в одно событие, пусть подписчик разбирается сам.

        Система спама, контролировать очень сложно.


      1. lair
        28.02.2017 11:16

        У меня было больше плохих кейсов — вида «надоело разбираться с MEF'ом (или любым другим DI-контейнером) и взаимодействовать через интерфейсы, сделаем ка мы штуку, которая броадкастит события, которые мы потом будем в ручную диспетчеризовать в получателях по строковым тегам».

        Так это же не EA. Броадкаст и диспетчеризация — вообще не EA уже.


        (ну и вообще, любой паттерн хорош тогда, когда он применен по месту)


        1. InWake
          03.03.2017 22:57

          ну и вообще, любой паттерн хорош тогда, когда он применен по месту

          да. Вот только EventAggregator так просто применить не по месту.


          1. lair
            03.03.2017 23:01

            Тем не менее, это не проблема паттерна. "Антипаттерн" — это когда любое применение, даже "по месту", несет больше проблем, чем пользы.


            1. InWake
              03.03.2017 23:25

              Сегодня паттерн применяешь по месту, а завтра его превращают в анти. Вот чья это проблема?


              1. lair
                04.03.2017 02:08
                +1

                Как можно "превратить паттерн в анти"?


                Но вообще, если кто-то приходит и меняет использование компонента, не проследив за существующими пользователями — он, как бы, не прав. Не надо контракт нарушать.


  1. NLO
    00.00.0000 00:00

    НЛО прилетело и опубликовало эту надпись здесь