EventAggregator-アンチパターン

読む前に、 EventAggregatorテンプレートについて読む必要があります。 EventAggregatorは、弱い接続を介して、複合アプリケーションのコンポーネントとサービスの相互作用を提供します。



EventAggregatorは、多くのWPFフレームワークで見つけることができます:Mvvm LightクラスMessenger 、CatelクラスMessageMediator 。 WPF PrismフレームワークでEventAggregatorに会いました。 EventAggregatorの使用は、シンプルで柔軟であることが実証されています。 システムコンポーネントは互いに独立します。あるコンポーネントを変更しても、別のコンポーネントを壊すことを恐れません。



個々のコンポーネントを検討する場合、これはすべて真実ですが、システム内のコンポーネントのレベルまで上昇すると、深刻な問題が発生する可能性があります。









私は、システムの部分間の接続が弱すぎ、暗黙の相互作用があるという私の見解を共有しています。



EventAggregatorによるLED制御



LEDを制御するには、電源ボタン- 電源 、2つの状態のスイッチ - スイッチと2つのLED- RedLedおよびBlueLedが必要です。 WPFでは、次のようになります。









電源ボタンは、 スイッチの状態に応じてLEDの1つを点灯します



EventAggregatorに基づくシステムでは、電源オン/オフ-PowerEventとスイッチ状態変更-SwitchEventの 2つのイベントを区別します。



PowerEventイベントは、電源ボタンが押されると発行され、SwitchEventイベントは、スイッチが押されると発行されます。 LEDは、PowerEventおよびSwitchEventイベントにサブスクライブします。



電源が入っていて、スイッチが目的の状態にある場合、LEDが点灯します。



イベントコード
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); } } }
      
      







LEDコード
 /// <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つのコンポーネントしかありませんが、LEDを別の要素に置き換えるには何をする必要がありますか? LedViewModelからサブスクリプションをコピーします-複製します。



コンポーネントを動的に置き換えると、サブスクリプションの解除を複製する必要があるすべての場所でさらに悪化します。 EventAggregatorはデフォルトで弱参照を作成します。 Weakreferenceを使用すると、登録解除は自動的に行われますが、コンポーネントを動的に置き換えると、登録がいつ削除されるかはわかりません-明示的な登録解除を複製する必要があるすべての場所。



コンポーネントを交換しても、システムの状態がわかりません。電源が入っているかどうか、スイッチがどの位置にあるか、どこから入手できるかわかりません。 1つの解決策は、システムに補助イベントを導入することです。 補助イベントは、コンポーネントにイベントの発行を要求します-PowerEventおよびSwitchEvent。 これで、このイベントの発行とサブスクライブを処理する必要があるすべての場所で、システムが分割されてWebになります。



システムコンポーネントはEventAggregatorについてのみ知っていますが、これは疎結合を意味しますか? いや コンポーネントは互いに分離されていますが、システムには非常に強力な暗黙の接続が存在します。 強い接続は、処理が必要なイベントのセットで表されます。 Ledを変更せずに、スイッチを別のコンポーネントに置き換えることはできません。 その結果、システムの部分間の接続はノードに変わります:強く、暗黙的で混乱します。



回路に複数のスイッチがあるようにするために何をする必要がありますか?



答えを得る前に、よく考えてください。




インターフェースを実装し、構成に応じて置き換えられるサービス内でのEventAggregatorの使用について...思い出さない方がいいでしょう。



問題はどこから発生しますか?



EventAggregatorを使用すると、5つのSOLID原則のうち3つに違反します。 責任の一意性-サブスクリプション/サブスクリプション解除は、スキームのコンポーネントの懸念ではありません。 オープン性が閉じられている-コンポーネントの相互作用スキームを変更する場合、サブスクリプション/サブスクライブ解除を編集する必要があります。 依存関係の反転-コンポーネントは、サブスクライブ/サブスクライブ解除するイベントを決定します。

5つのうち3つ、しかし問題...



PS EventAggregatorは慎重に使用してください。 私にとって、EventAggregatorはアンチパターンであり、そこからのトラブルはそれ以上のものです。



All Articles