SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

DeveloperZine(デベロッパージン)- エンジニアの意思決定を支える技術情報メディア ProductZine

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

japan.internet.com翻訳記事

階層化アーキテクチャと依存性注入・依存性逆転

アプリケーションを階層化して構築する考え方の紹介

サービスロケータによる依存性注入

 IEmployeeTaskのどの実装を使用すべきかプレゼンターが知る必要性を完全になくすためには、新しい要素を追加しなければなりません。依存性注入の世界では、サービスロケータとは他のオブジェクトの依存物を取得する方法を知っているオブジェクトにすぎません。今回のシナリオにおけるサービスロケータの役割は、プレゼンターが扱えるIEmployeeTaskの実装を見つけることです。ここまで読んできた方は「つまり、プレゼンターはこのサービスロケータへの依存を持つことになるのか」と思ったことでしょう。そのとおりです。「それなら、サービスロケータを受け入れるためにプレゼンターのコンストラクタを変更する必要があるのだろうか」と思った方もいるでしょう。そういう方法もあります。しかしそうすると、他のクラスでもロケータのサービスを利用して依存を解決したくなった場合に、それらのクラスについてもサービスロケータへのインターフェイスを受け入れるコンストラクタが必要になるでしょう。そうなると、アーキテクチャへの侵害になりそうですし、必要以上の作業を強いられそうにも見えます。

 私が考えているソリューションは、実際のプロジェクトでサービスロケータを利用してみたいと思う読者なら、すぐに採用できるものです。また、Windsor Castleのような本格的な依存性注入フレームワークを使い始めるときに応用できるソリューションでもあります(Windsor Castleは有名なオープンソースの依存性注入フレームワークです)。まずサービスロケータのインターフェイスを作成することにします。

public interface IDependencyResolver :
   IDisposable
{
void RegisterImplmentationOf<T>(
   T component);
   T GetImplementationOf<T>();
}

 このコードでは、クライアントのメソッドからロケータを呼び出すときにキャストする必要がないように汎用メソッドを使用しています。前述のとおり、今回のシナリオでは、サービスロケータへの明示的なパラメータ依存を持たなくても、サービスロケータによって提供される機能を既存のクラスでコンシュームできるようにしたいと考えています。これを実現するために、すべての呼び出しをサービスロケータインターフェイスの任意の具象実装に委任する静的クラスを使用します。

public static  class DependencyResolver
{
   private static IDependencyResolver 
     resolver;
   public static void RegisterResolver(
     IDependencyResolver resolver)
   {
      DependencyResolver.resolver = resolver;    
   }
   public static T GetImplementationOf<T>()
   {
      return resolver.GetImplementationOf<T>();
   }
}

 DependencyResolverクラスにRegisterResolverというメソッドがあることに注目してください。このメソッドには、自らの呼び出しの転送先となるIDependencyResolverの実装が渡されます。これは、プレゼンターの観点からすると、IEmployeeTaskのEmployeeTask実装の詳細を知らなくても、DependencyResolverクラスを使用してIEmployeeTaskの実装を探し出せるということを意味します。これに基づき、EmployeePresenterのコンストラクタは次のものから、

Public ViewEmployeesPresenter(
   IEmployeeView view):this(
   view,new EmployeeTask())
{            
}
   
public ViewEmployeesPresenter(
   IEmployeeView view, 
   IEmployeeTask task)

 次のように変更されます。

public ViewEmployeesPresenter(
   IEmployeeView view):this(view,
   DependencyResolver.GetImplementationOf
   <IEmployeeTask>())
{            
}
public ViewEmployeesPresenter(
   IEmployeeView view, 
   IEmployeeTask task)

 前述の「完全版」コンストラクタを引き続き使用していることに注目してください。このコンストラクタは、プレゼンターの動作を検証するためのテストに利用できます。Webページで使用するコンストラクタでは、プレゼンターはDependencyResolverを使用し、IEmployeeTaskの実装を取得するように要求します。これで、プレゼンターとEmployeeTaskを結合する部分は何もなくなります。

 今回の例では、静的なDependencyResolverクラスを使用して、プレゼンターをサービスロケータに結合しました。「静的クラスなんてとんでもない」とか「シングルトンはだめだ」という声が聞こえてきそうですが、静的クラスもシングルトンも、テスト容易性を考慮したものならばそう問題にはなりません(従来のシングルトンや静的クラスの実装でこれを扱うのは簡単ではありません)。今回のシナリオでは、DependencyResolver静的クラスは自分の仕事を(テスト時に簡単に偽造できる)IDependencyResolver実装に委任するだけなので、テストを容易に実行できます。

 IDependencyResolverインターフェイスを使用することの利点は、サービスロケータを使用する道を選んだときに、アプリケーションの起動時に設定される独自のサービスロケータを作成できることです。リスト5は、そのようなロケータのコードを示しています。以下は、アプリケーションの起動時に呼び出されるクラスで独自のサービスロケータを設定する例です。

public class ApplicationStartupTask
{        
   public static void Initialize()
   {
      IDependencyResolver resolver = new CustomDependencyResolver();
      resolver.RegisterImplmentationOf<IEmployeeTask>
         (new EmployeeTask());
      DependencyResolver.RegisterResolver(resolver);
   }
}

 IEmployeeTaskの実装にEmployeeTaskオブジェクトを返すよう求めるタイミングをサービスロケータに明確に指示していることに注目してください。また、この実装はEmployeeTaskに対して、たとえシングルトンとして明示的に指定されていなくても、このアプリケーションでシングルトンになるように強要してもいます。最後のステップでは、サービスロケータをDependencyResolverに登録して、すべてのクライアントがその機能にアクセスできるようにしています。

 読者が自分のサービスロケータにもっと多くの機能を求めるとしても、自分でそれをコーディングしたくはないでしょう。IDependencyResolverインターフェイスを使用すれば、既存の依存性注入フレームワークを利用している実装へと簡単に切り替えることができます。リスト6は、Windsor Castleを使用するようにコーディングされたIDependencyResolver実装の例です。

リスト6 独自のサービスロケータの実装
public class CustomDependencyResolver : IDependencyResolver
{
   private IDictionary<Type, object> components;
   public CustomDependencyResolver()
   {
      components = new Dictionary<Type, object>();
   }
   public void RegisterImplmentationOf<T>(T component)
   {
      components.Add(typeof (T), component);
   }
   public T GetImplementationOf<T>()
   {
      return (T) components[typeof (T)];
   }
   public void Dispose()
   {
      components.Clear();  
   }
}

 インターフェイスへのコーディングのメリットは、Windsorやその他の依存性注入フレームワークに切り替える場合に、クライアントコードの変更がごくわずかで済むことです。実際、Windsorを使用するために必要な変更は、ApplicationStartupTaskクラスを次のように変更することだけです。

public class ApplicationStartupTask
{
   private static readonly string 
      ContainerConfigurationPath = 
      Path.Combine(AppDomain.
      CurrentDomain.BaseDirectory, 
      "container.boo");
   public static void Initialize()
   {
      IWindsorContainer container = new 
         WindsorContainer();
      BooReader.Read(container, ContainerConfigurationPath);
      DependencyResolver.RegisterResolver(
         new WindsorDependencyContainer(container));
   }
}

 Windsorのようなフル装備のフレームワークを使用することの利点は、依存物の特定の実装に縛られたコードがまったく(ApplicationStartUpの中にさえ)なくなることです。依存物を必要とするオブジェクトは、それを取得するように引き続きDependencyResolverクラスに要求することができ、DependencyResolverはその要求を満たすために与えられたIDependencyResolverの実装を、それがどんな実装でも使用します。

 本稿のサンプルコードでは、私はBinsorを使ってWindsorコンテナを設定しています。BinsorはXMLファイルを使わずにWindsorを設定するためのツールで、Ayende Rahienによって開発されました。これを使用すると、アプリケーションの外部に格納されたコンフィグレーションを使って依存を設定することができます。

まとめ

 本稿では多くの問題を論じました。初めに、よく知られている多くのベストプラクティスに反する単層アプリケーションを取り上げ、階層化アーキテクチャといくつかのリファクタリング手法、さらに依存性逆転の原則について説明しました。実装へのコーディングに比べて抽象へのコーディングを行った方が有利である理由についても触れました。さらに、抽象へのコーディングの利点をよりよく理解していただくために、依存性注入とサービスロケータという概念を紹介しました。

 本稿で紹介した内容が、読者の皆さんのアプリケーションアーキテクチャでより緩やかに結合されたソリューションを実現するためのお役に立てば幸いです。

編集者注
 この記事はもともとCoDe MagazineのMay/June 2007年5-6月号に掲載されたものですが、許可を得てここに転載しました。

この記事は参考になりましたか?

連載通知を行うには会員登録(無料)が必要です。
既に会員の方はを行ってください。
japan.internet.com翻訳記事連載記事一覧

もっと読む

この記事の著者

japan.internet.com(ジャパンインターネットコム)

japan.internet.com は、1999年9月にオープンした、日本初のネットビジネス専門ニュースサイト。月間2億以上のページビューを誇る米国 Jupitermedia Corporation (Nasdaq: JUPM) のニュースサイト internet.comEarthWeb.com からの最新記事を日本語に翻訳して掲載するとともに、日本独自のネットビジネス関連記事やレポートを配信。

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

Jean-Paul S. Boodhoo(Jean-Paul S. Boodhoo)

.NET 1.0のベータ1から.NET Frameworkに従事してきた.NET開発のエキスパートで、アプリケーションのアーキテクチャ作成と設計と開発で7年以上の経験がある。アジャイルプラクティスと実際的なビヘイビア駆動開発(BDD)テクニックを通じてチームの成功を支援する独立コンサルタントとして活躍している。BDDを.NETに応用する記事をVisual Studio Magazine、DevX、MSDNに寄稿。ポッドキャス...

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

CodeZine(コードジン)
https://codezine.jp/article/detail/1622 2007/09/04 14:00

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー