特集
.NET開発者のためのDI&AOP入門(前編)

Seasar.NETでDIを始めよう

株式会社アイビス 杉本 和也
2007/12/10
Page1 Page2 Page3 Page4

2-2. DIの実践

 ここではWindowsアプリケーションのサンプルでDIを実践してみよう。Windowsアプリケーションのプロジェクトを作成すると、「Form1」というクラス(Windowsフォーム)が作成されるので、下の画面のようにコントロールを配置しよう。

 これから作成するサンプルは、社員コードを入力して社員検索ボタンをクリックすると社員名が表示されるという簡単なものだ。

サンプル・アプリケーションのWindowsフォームのデザイン
配置するコントロールとそのプロパティを下表に記す。
コントロール プロパティ名
Label (Name) empCodeLabel
Text 社員コードを入力してください
Label (Name) empNameLabel
Text 検索結果:社員名を表示します
TextBox (Name) empCodeTextBox
TextBox (Name) empNameTextBox
ReadOnly True
Button (Name) searchButton
Text 社員検索

 これから作成するクラスやインターフェイスを下の図に示す。

サンプル・アプリケーションのクラスやインターフェイス
  • Form1クラスにIEmployeeLogic型のフィールドEmpLogicを作成する。そこにIEmployeeLogic実装クラス(=EmployeeLogicクラス)のインスタンスがDIされ、Form1クラスからEmployeeLogicクラスの機能を利用することができるようになる。

  • 同じようにEmployeeLogicクラスにIEmployeeDao型のフィールドEmpDaoを作成する。そこにIEmployeeDao実装クラス(=EmployeeDaoクラス)のインスタンスがDIされ、EmployeeLogicクラスからEmployeeDaoクラスの機能を利用することができるようになる。

 それでは上の図のインターフェイスとクラスを作成しよう。以下にそのすべてのソース・ファイルの内容を掲載する。

namespace QuillSample
{
  public interface IEmployeeDao
  {
    /// <summary>
    /// 社員コードから社員名を取得する
    /// </summary>
    /// <param name="empNo">社員コード</param>
    /// <returns>社員名</returns>
    string GetEmpName(int empNo);
  }
}
IEmployeeDaoインターフェイスのソース・コード(IEmployeeDao.cs)

namespace QuillSample
{
  public class EmployeeDao : IEmployeeDao
  {
    public string GetEmpName(int empNo)
    {
      // 本来はデータベースなどから取得するがDIが目的なので
      // パラメータの社員コードにかかわらず「山田太郎」固定で返す
      return "山田太郎";
    }
  }
}
EmployeeDaoクラスのソース・コード(EmployeeDao.cs)

namespace QuillSample
{
  public interface IEmployeeLogic
  {
    /// <summary>
    /// 社員コードから社員名を敬称付きで取得する
    /// </summary>
    /// <param name="empNo">社員コード</param>
    /// <returns>敬称付きの社員名</returns>
    string GetEmpNameWithTitle(int empNo);
  }
}
IEmployeeLogicインターフェイスのソース・コード(IEmployeeLogic.cs)

namespace QuillSample
{
  public class EmployeeLogic : IEmployeeLogic
  {
    public IEmployeeDao EmpDao;

    public string GetEmpNameWithTitle(int empNo)
    {
      string empName = EmpDao.GetEmpName(empNo) + "さん";
      return empName;
    }
  }
}
EmployeeLogicクラスのソース・コード(EmployeeLogic.cs)

using System;
using System.Windows.Forms;

namespace QuillSample
{
  public partial class Form1 : Form
  {
    public IEmployeeLogic EmpLogic;

    public Form1()
    {
      InitializeComponent();
    }

    // searchButtonのClickイベントに割り当てる
    private void searchButton_Click(object sender, EventArgs e)
    {
      // DIが目的なので入力チェックは省略する
      int empNo = int.Parse(empCodeTextBox.Text);

      // 社員コードから敬称付きの社員名を取得する
      string empName = EmpLogic.GetEmpNameWithTitle(empNo);

      // 敬称付きの社員名をTextBoxに表示する
      empNameTextBox.Text = empName;
    }
  }
}
Form1クラスのソース・コード(Form1.cs)

 以上のソース・ファイルを実装すると、[ソリューション エクスプローラ]の内容は次のようになる。

サンプル・アプリケーションの[ソリューション エクスプローラ]の内容

 これでロジック部分は完成したが、このまま実行して社員検索ボタンをクリックしてもNullReferenceException例外が発生して動作しない。EmpLogicフィールドがnullのままだからである。

 ここでDIコンテナであるQuillの出番である。Quillでは、インターフェイスにImplementation属性(Seasar.Quill.Attrs名前空間)を設定して、そこでDIすべき実装クラスを指定する。

 それではIEmployeeDaoインターフェイスとIEmployeeLogicインターフェイスにImplementation属性を設定してみよう。ちなみにフィールドはprotectedスコープ以上にする必要がある。

using Seasar.Quill.Attrs;

namespace QuillSample
{
  [Implementation(typeof(EmployeeDao))]
  public interface IEmployeeDao
  {
    ……省略……
IEmployeeDaoインターフェイスへのImplementation属性の指定(IEmployeeDao.cs)

using Seasar.Quill.Attrs;

namespace QuillSample
{
  [Implementation(typeof(EmployeeLogic))]
  public interface IEmployeeLogic
  {
    ……省略……
IEmployeeLogicインターフェイスへのImplementation属性の指定(IEmployeeLogic.cs)

 Implementation属性でDIすべき実装クラスを指定すれば、後はQuillInjectorクラス(Seasar.Quill名前空間)のInjectメソッドに、DIを行いたいオブジェクトを渡すだけである。Injectメソッドを呼び出すには、QuillInjectorクラスのオブジェクトが必要だが、これはQuillInjectorクラスの静的メソッドGetInstanceで取得できる。

 今回の場合はForm1クラスのコンストラクタで、Injectメソッドを呼び出して、ここでDIを行いたいオブジェクトであるForm1オブジェクト(this)をDIしてみる。QuillによりEmpLogicフィールドに実装クラスのインスタンスがセットされるはずである。また、Injectメソッドに渡されたオブジェクトは連鎖的にDIが行われる。そのためEmpLogicフィールドにセットされたEmployeeLogicのインスタンスのEmpDaoフィールドにも実装クラスのインスタンスがセットされる。

using System;
using System.Windows.Forms;
using Seasar.Quill;

namespace QuillSample
{
  public partial class Form1 : Form
  {
    public IEmployeeLogic EmpLogic;

    public Form1()
    {
      InitializeComponent();

      // DIを行う
      QuillInjector.GetInstance().Inject(this);
    }
    ……省略……
InjectメソッドによるDIを行いたいオブジェクトを渡すだけ(Form1.cs)

 それでは実行してみよう。社員コードに何か数字を入力して社員検索ボタンをクリックすると、今度はNullReferenceException例外が発生せずに、検索結果を表示するTextBox(empNameTextBox)に「山田 太郎さん」と表示されたはずである。

 このようにQuillを用いることで、ロジック部分は実装クラスに依存しないコーディングを行うことができる。ただしImplementation属性に実装クラスを指定する必要があるので、この部分に関しては依存が存在してしまう。ソース・コードを完全に依存しないようにしたい場合は、依存関係を設定ファイルに記述できるS2Containerを使うとよい。

 ちなみに、もしインターフェイスを作成せずにクラスのみでDIを行う場合は、クラスにパラメータなしのImplementation属性を設定すればよい。

 また、Quillがオブジェクトのインスタンスを作成してDIを行うわけだが、1度作成したインスタンスと同じ型のDIを要求された場合には、以前作成したインスタンスがDIされる。インスタンスの管理を行うDIコンテナは、普通、いくつかのインスタンス・モードを提供するが、Quillは常に同じインスタンスを返すsingletonモードしか提供していない。これはQuillがステートレスな(=状態をインスタンス変数に持たない)業務ロジックを構築するために作成されたからである。そのためQuillを利用する場合には常にステートレスなロジックの設計を行う必要がある。

 最後にDIが有効となる例を取り上げよう。


 INDEX
  [特集].NET開発者のためのDI&AOP入門
  Seasar.NETでDIを始めよう
    1.DI&AOP開発環境の準備
    2.DIとAOPを理解するためのサンプルの作成
  3.ソース・コードで理解するDI
    4.DIが役立つ場面
 
  コードで簡単に分かる“AOP”
    1.ソース・コードで理解するAOP
    2.AOPの実践


Insider.NET フォーラム 新着記事
  • 第2回 簡潔なコーディングのために (2017/7/26)
     ラムダ式で記述できるメンバの増加、throw式、out変数、タプルなど、C# 7には以前よりもコードを簡潔に記述できるような機能が導入されている
  • 第1回 Visual Studio Codeデバッグの基礎知識 (2017/7/21)
     Node.jsプログラムをデバッグしながら、Visual Studio Codeに統合されているデバッグ機能の基本の「キ」をマスターしよう
  • 第1回 明瞭なコーディングのために (2017/7/19)
     C# 7で追加された新機能の中から、「数値リテラル構文の改善」と「ローカル関数」を紹介する。これらは分かりやすいコードを記述するのに使える
  • Presentation Translator (2017/7/18)
     Presentation TranslatorはPowerPoint用のアドイン。プレゼンテーション時の字幕の付加や、多言語での質疑応答、スライドの翻訳を行える
@ITメールマガジン 新着情報やスタッフのコラムがメールで届きます(無料)

注目のテーマ

Insider.NET 記事ランキング

本日 月間