依存関係の挿入
Note
この電子ブックは 2017 年の春に公開され、それ以来更新されていません。 貴重なままの本に多くがありますが、資料の一部は時代遅れです。
通常、クラス コンストラクターは、オブジェクトをインスタンス化するときに呼び出され、オブジェクトに必要な値はすべて引数としてコンストラクターに渡されます。 これは依存関係の挿入の例であり、具体的には コンストラクターの挿入と呼ばれます。 オブジェクトに必要な依存関係は、コンストラクターに挿入されます。
依存関係をインターフェイス型として指定することで、依存関係の挿入により、これらの型に依存するコードから具象型を分離できます。 通常、インターフェイスと抽象型の登録とそれらの間のマッピングの一覧、およびこれらの型を実装または拡張する具象型を保持するコンテナーが使用されます。
プロパティ セッターの挿入、メソッド呼び出しの挿入など、他の種類の依存関係の挿入もありますが、あまり一般的ではありません。 このため、この章では、依存関係の挿入コンテナーを使用したコンストラクター挿入の実行にのみ焦点を当てます。
依存関係の挿入の概要
依存関係の挿入は、制御の反転 (IoC) パターンの特殊なバージョンであり、反転される懸念事項は、必要な依存関係を取得するプロセスです。 依存関係の挿入を使用すると、実行時に依存関係をオブジェクトに挿入する役割を別のクラスが担います。 次のコード例は、依存関係の挿入を使用する場合に ProfileViewModel
クラスを構造化する方法を示しています。
public class ProfileViewModel : ViewModelBase
{
private IOrderService _orderService;
public ProfileViewModel(IOrderService orderService)
{
_orderService = orderService;
}
...
}
コンストラクターは ProfileViewModel
、インスタンスを IOrderService
引数として受け取り、別のクラスによって挿入されます。 クラスの唯一の ProfileViewModel
依存関係はインターフェイス型にあります。 そのため、 ProfileViewModel
クラスには、オブジェクトのインスタンス化 IOrderService
を担当するクラスの知識がありません。 オブジェクトをインスタンス化 IOrderService
し、 クラスに ProfileViewModel
挿入するクラスは、 依存関係挿入コンテナーと呼ばれます。
依存関係の挿入コンテナーにより、クラス インスタンスをインスタンス化し、コンテナーの構成に基づいてインスタンスの有効期間を管理するための機能が提供され、オブジェクト間の結合が削減されます。 オブジェクトの作成時に、コンテナーはオブジェクトに必要な依存関係を挿入します。 これらの依存関係はまだ作成されていない場合、コンテナーでは最初に依存関係を作成して解決します。
注意
依存関係の挿入は、ファクトリを使用して手動で実装することもできます。 ただし、コンテナーを使用すると、有効期間管理やアセンブリ スキャンによる登録などの追加機能が提供されます。
依存関係の挿入コンテナーの使用には、いくつかの利点があります。
- コンテナーを使用すると、クラスが依存関係を見つけて有効期間を管理する必要がなくなります。
- コンテナーを使用すると、 クラスに影響を与えることなく、実装された依存関係をマッピングできます。
- コンテナーを使用すると、依存関係をモックできるようになり、テストの容易性が向上します。
- コンテナーを使用すると、新しいクラスをアプリに簡単に追加できるようになり、保守性が向上します。
MVVM を使用するアプリの Xamarin.Forms コンテキストでは、依存関係挿入コンテナーは通常、ビュー モデルの登録と解決、およびサービスの登録とビュー モデルへの挿入に使用されます。
アプリ内のビュー モデルとサービス クラスのインスタンス化を管理するために TinyIoC を使用する eShopOnContainers モバイル アプリでは、多くの依存関係挿入コンテナーを使用できます。 TinyIoC は、多数の異なるコンテナーを評価した後に選択されました。また、モバイル プラットフォームでは、既知のコンテナーの大部分と比較して優れたパフォーマンスが特徴です。 疎結合アプリの構築が容易になり、依存関係挿入コンテナーで一般的に見られるすべての機能が提供されます。これには、型マッピングの登録、オブジェクトの解決、オブジェクトの有効期間の管理、解決するオブジェクトのコンストラクターへの依存オブジェクトの挿入などのメソッドが含まれます。 TinyIoC の詳細については、「 tinyIoC on github.com」を参照してください。
TinyIoC では、型によって TinyIoCContainer
依存関係の挿入コンテナーが提供されます。 図 3-1 は、このコンテナーを使用するときの依存関係を示しています。これにより、オブジェクトが IOrderService
インスタンス化され、 クラスに挿入されます ProfileViewModel
。
図 3-1: 依存関係の挿入を使用する場合の依存関係
実行時に、コンテナーはオブジェクトをインスタンス化する前に IOrderService
、インスタンス化する必要があるインターフェイスの実装を認識する ProfileViewModel
必要があります。 これには以下が含まれます。
- インターフェイスを実装するオブジェクトをインスタンス化する方法を決定する
IOrderService
コンテナー。 これは "登録" と呼ばれます。 - インターフェイスを実装する オブジェクトと オブジェクトを
IOrderService
インスタンス化するProfileViewModel
コンテナー。 これは "解決" と呼ばれます。
最終的に、アプリは オブジェクトの使用を ProfileViewModel
終了し、ガベージ コレクションで使用できるようになります。 この時点で、他のクラスが同じインスタンスを IOrderService
共有していない場合、ガベージ コレクターはインスタンスを破棄する必要があります。
ヒント
コンテナーに依存しないコードを記述します。 常に、使用されている特定の依存関係コンテナーからアプリを切り離すために、コンテナーに依存しないコードを記述してみてください。
登録
依存関係をオブジェクトに挿入するには、最初に依存関係の型をコンテナーに登録する必要があります。 型を登録するには、通常、コンテナーにインターフェイスと、インターフェイスを実装する具象型を渡す必要があります。
コードを使用して型とオブジェクトをコンテナーに登録するには、次の 2 つの方法があります。
- 型またはマッピングをコンテナーに登録します。 必要に応じて、コンテナーにより、指定された型のインスタンスが構築されます。
- コンテナー内の既存のオブジェクトをシングルトンとして登録します。 必要に応じて、コンテナーにより、既存のオブジェクトへの参照が返されます。
ヒント
依存関係の挿入コンテナーがいつでも適しているとは限りません。 依存関係の挿入によって複雑さが増し、要件が増えるため、小さなアプリには適さない、または役に立たない可能性があります。 クラスに依存関係がない場合、または他の型の依存関係ではない場合、それをコンテナーに格納しても意味がない可能性があります。 さらに、型に不可欠な単一の依存関係セットがあり、変更されることがない場合も、それをコンテナーに格納しても意味がない可能性があります。
依存関係の挿入を必要とする型の登録は、アプリ内の 1 つのメソッドで実行する必要があります。このメソッドは、アプリのライフサイクルの早い段階で呼び出して、アプリがクラス間の依存関係を認識していることを確認する必要があります。 eShopOnContainers モバイル アプリでは、これは クラスによって ViewModelLocator
実行されます。このクラスは オブジェクトを TinyIoCContainer
ビルドし、そのオブジェクトへの参照を保持するアプリ内で唯一のクラスです。 次のコード例は、eShopOnContainers モバイル アプリが クラス内の オブジェクトを宣言する TinyIoCContainer
方法を ViewModelLocator
示しています。
private static TinyIoCContainer _container;
型はコンストラクターに ViewModelLocator
登録されます。 これは、最初にインスタンスを TinyIoCContainer
作成することによって実現されます。これは、次のコード例で示されています。
_container = new TinyIoCContainer();
その後、型が オブジェクトに TinyIoCContainer
登録され、次のコード例は、型登録の最も一般的な形式を示しています。
_container.Register<IRequestProvider, RequestProvider>();
ここで示すメソッドは Register
、インターフェイス型を具象型にマップします。 既定では、各インターフェイス登録はシングルトンとして構成され、依存するすべてのオブジェクトが同じ共有インスタンスを受け取ります。 そのため、コンテナーには 1 つの RequestProvider
インスタンスのみが存在します。これは、コンストラクターを介した の IRequestProvider
挿入を必要とするオブジェクトによって共有されます。
具象型は、次のコード例に示すように、インターフェイス型からのマッピングなしで直接登録することもできます。
_container.Register<ProfileViewModel>();
既定では、各具象クラスの登録は、すべての依存オブジェクトが新しいインスタンスを受け取るようにマルチインスタンスとして構成されます。 したがって、 が解決されると ProfileViewModel
、新しいインスタンスが作成され、コンテナーによって必要な依存関係が挿入されます。
解決策
型は、登録された後、依存関係として解決または挿入できます。 型が解決され、コンテナーが新しいインスタンスを作成する必要がある場合、インスタンスに依存関係が挿入されます。
通常、型が解決されると、次の 3 つのいずれかが発生します。
- 型が登録されていない場合、コンテナーによって例外がスローされます。
- 型がシングルトンとして登録されていない場合、コンテナーによってシングルトン インスタンスが返されます。 この型が初めて呼び出される場合、コンテナーは必要に応じてそれを作成し、それに対する参照を保持します。
- 型がシングルトンとして登録されていない場合、コンテナーは新しいインスタンスを返し、それに対する参照を保持しません。
次のコード例は、以前に RequestProvider
TinyIoC に登録された型を解決する方法を示しています。
var requestProvider = _container.Resolve<IRequestProvider>();
この例では、TinyIoC は型の具象型と依存関係を IRequestProvider
解決するように求められます。 通常、 メソッドは、 Resolve
特定の型のインスタンスが必要な場合に呼び出されます。 解決されたオブジェクトの有効期間の制御については、「解決されたオブジェクト の有効期間の管理」を参照してください。
次のコード例は、eShopOnContainers モバイル アプリがビュー モデルの種類とその依存関係をインスタンス化する方法を示しています。
var viewModel = _container.Resolve(viewModelType);
この例では、TinyIoC は要求されたビュー モデルのビュー モデルの種類を解決するように求められ、コンテナーは依存関係も解決します。 型を解決するProfileViewModel
場合、解決する依存関係はオブジェクトとIOrderService
オブジェクトですISettingsService
。 インターフェイスの登録は クラスと クラスのSettingsService
登録時に使用されたので、TinyIoC は クラスと OrderService
クラスのシングルトン インスタンスをSettingsService
返し、クラスのProfileViewModel
コンストラクターに渡OrderService
します。 eShopOnContainers モバイル アプリがビュー モデルを構築し、ビューに関連付ける方法の詳細については、「 ビュー モデルとビュー モデル ロケーターを自動的に作成する」を参照してください。
注意
コンテナーで型を登録し、解決すると、パフォーマンス コストが発生します。特に、アプリのページ ナビゲーションごとに依存関係が再構築される場合には、各型を作成するために、コンテナーでリフレクションが使用されるからです。 依存関係が多いか深い場合、作成コストが大幅に増加する可能性があります。
解決されたオブジェクトの有効期間の管理
具象クラス登録を使用して型を登録した後、TinyIoC の既定の動作では、型が解決されるたびに、または依存関係メカニズムによってインスタンスが他のクラスに挿入されるたびに、登録済み型の新しいインスタンスが作成されます。 このシナリオでは、コンテナーは解決されたオブジェクトへの参照を保持しません。 ただし、インターフェイス登録を使用して型を登録する場合、TinyIoC の既定の動作は、オブジェクトの有効期間をシングルトンとして管理することです。 そのため、コンテナーがスコープ内にある間、インスタンスはスコープ内に残り、コンテナーがスコープ外に出てガベージ コレクションされた場合、またはコードによってコンテナーが明示的に破棄されるときに破棄されます。
既定の TinyIoC 登録動作は、fluent AsSingleton
メソッドと AsMultiInstance
API メソッドを使用してオーバーライドできます。 たとえば、 メソッドを AsSingleton
メソッドと共に Register
使用して、 メソッドを呼び出 Resolve
すときにコンテナーが型のシングルトン インスタンスを作成または返すようにすることができます。 次のコード例は、 クラスのシングルトン インスタンスを作成するように TinyIoC に指示する方法を LoginViewModel
示しています。
_container.Register<LoginViewModel>().AsSingleton();
型が LoginViewModel
初めて解決されると、コンテナーによって新しい LoginViewModel
オブジェクトが作成され、そのオブジェクトへの参照が保持されます。 以降の の解決では、 LoginViewModel
コンテナーは以前に作成された オブジェクトへの参照を LoginViewModel
返します。
注意
シングルトンとして登録された型は、コンテナーが破棄されるときに破棄されます。
まとめ
依存関係の挿入により、これらの型に依存するコードから具象型を分離できます。 通常、インターフェイスと抽象型の登録とそれらの間のマッピングの一覧、およびこれらの型を実装または拡張する具象型を保持するコンテナーが使用されます。
TinyIoC は、よく知られているコンテナーの大部分と比較して、モバイル プラットフォームで優れたパフォーマンスを備えた軽量コンテナーです。 疎結合アプリの構築が容易になり、依存関係挿入コンテナーで一般的に見られるすべての機能が提供されます。これには、型マッピングの登録、オブジェクトの解決、オブジェクトの有効期間の管理、解決するオブジェクトのコンストラクターへの依存オブジェクトの挿入などのメソッドが含まれます。