多くのアプリケーションで独立して使用されるフレームワークの一部であるウィジェットクラスがあるとします。私は多くの状況でウィジェットインスタンスを作成し、それらの寿命は異なります。ウィジェットのインスタンス指定メソッドに加えて、以下のクラス全体の操作を実行できるようにしたいと思います。
これらの操作をサポートするために、私は2つのアプローチを検討してきました。
コンテナクラス -いくつかのコンテナーまたはマネージャークラスWidgetContainerを作成します。これは、すべてのウィジェットインスタンスのリストを保持し、反復をサポートし、ウィジェットの追加、削除、およびルックアップのメソッドを提供します。たとえばC#の場合:
public class WidgetContainer:IEnumerable&ltWidget> { public void AddWidget(Widget); public Widget GetWidget(WidgetId id); public void RemoveWidget(WidgetId id); }
静的クラスメソッド-ウィジェットに静的クラスメソッドを追加します。例えば:
パブリッククラスウィジェット { public Widget(WidgetId id); public static Widget GetWidget(WidgetId id); public static void RemoveWidget(WidgetId id); public static IEnumerable&ltWidget> AllWidgets(); }
コンテナークラスを使用すると、コンテナークラスにアクセスする方法の問題が追加されます。シングルトンにしますか?.. yuck!そのようなすべてのコンテナークラスへのアクセスを提供するいくつかのWorldオブジェクトを作成しますか?
コンテナークラスのアプローチを使用する多くのフレームワークを見てきたので、一般的なコンセンサスは何ですか?
現代のコンセンサスは、これをまったく行うべきではないということです。
「このクラスのすべてのインスタンス」の設計を持つことは、多くの面で厄介であることが証明されています。最も重要なのは同時実行性です。すべてのウィジェット、またはスレッドが所有するすべてのウィジェットが必要ですか?すべてのウィジェットリストをスレッドセーフにしますか?単体テストはほとんどの場合並行して行われるため、「私のアプリはシングルスレッド」では十分でない場合があります。
あなたが遭遇する他の問題はあなたのデザインにあります。グローバル、静的、シングルトンのいずれであっても、「唯一無二」のリストがあれば、複数が必要なときに問題にすぐに遭遇します。一時的なウィジェットやオフスクリーンウィジェットがリストに自動的に追加されると、それらの問題が発生します。 notが自動的に処理される場合、リストに追加するのを忘れたエラーが発生します。
「クラスのすべてのインスタンス」要件がないと、単純な古いコレクションになってしまいます。ウィジェットを使用するコードが何であっても(それがフレームワークである場合でも)、コレクションがどのように使用され、いつウィジェットがそこに配置されるかを調整できます。ウィジェット自体は気にする必要はありません。
これは、IoC(Inversion of Control)コンテナーを使用するのに適した候補のようです。コンテナーを自分で作成する代わりに、呼び出し可能なIoCライブラリの汎用コンテナー部分に依存するだけです。
たとえば、インターフェースを定義します。
public interface IWidgetHandler
{
void AddWidget(IWidget widget);
IWidget GetWidget(int widgetId);
void RemoveWidget(int widgetId);
IEnumerable<Widget> GetAllWidgets();
}
次に、そのインターフェースを実装するクラスを定義します。
public class WidgetHandler : IWidgetHandler
{
public void AddWidget(IWidget widget)
{
throw new NotImplementedException();
}
public IWidget GetWidget(int widgetId)
{
throw new NotImplementedException();
}
public void RemoveWidget(int widgetId)
{
throw new NotImplementedException();
}
public IEnumerable<Widget> GetAllWidgets()
{
throw new NotImplementedException();
}
}
WidgetHandlersのインスタンスを検索する方法をIoCコンテナーに登録します。
一般的に、IoCコンテナにはある種の登録メソッドがあるので、
MyIoCContainer.Instance.Register<IWidgetHandler, WidgetHandler>();
ウィジェットハンドラーが必要なときはいつでも、IoCにインスタンスのインスタンスを提供するように指示するだけです。
IWidgetHandler widgetHandler = MyIoCContainer.Instance.Resolve<IWidgetHandler>();
IoCコンテナーは、オブジェクトの存続期間も制御できます。
IoCに慣れるための詳細については、 ninjectのドキュメント を参照してください。他にもいくつかのオプションがあります( nity 、 autofac 、 Castle いくつか例を挙げると)。実際、すべてに共通のインターフェースを提供する CommonServiceLocator ライブラリがあり、有用な機能の共通点が最も低い(それらの間で交換したい場合)。
コードの一部がIWidgetHandlerに依存しているときにこれらをモックできるため、静的メソッドクラスと比較すると、コードをよりテストしやすくなります。実際のウィジェットハンドラーを提供する代わりに、IoC(またはテストフレームワーク)はFakeWidgetHandlerのインスタンスを提供できます。これは、コードへのさまざまな偽の応答を提供し、そのメソッドをアサートすることにより、このモジュールに依存するコードをテストするために使用できます。パラメータはIWidgetHandler依存関係に正しく渡されます。
幸運を!
シングルトンオブジェクトを使用することと、ウィジェットクラスで静的メソッドを使用することの間にまったく違いはありません。 Widget.GetWidgetとWidgetContainer.Instance.GetWidgetはまったく同じ問題を抱えています。
アプリケーションが実行されていない場合、ウィジェットはどこにどのような形で表示されますか?それはあなたの問題を解決するのに役立ちます。
それらがデータベースまたはファイルシステムにある場合、たとえば、それらにアクセスするために単一インスタンスのオブジェクトは必要ありません。任意の数のWidgetContainerオブジェクトが、ファイルシステムまたはデータベースから個別に取得できます。パフォーマンスが気になる場合は、キャッシングシステムを実装できます。