Factory Method (ファクトリメソッド) パターン

インスタンス生成を Factory(工場)に任せるデザインパターンです。
「普通に new すればいいじゃん」と思われるかもしれませんが、
new する前やした後に、あれやこれややって、やっとメインロジックで使える形になるクラスだとしたら?
メインロジックの色々な所で、一々それをやるのは嫌ですよね。


また、 Factory(工場)によって、抽象化されたオブジェクトを生産してもらい、
メインロジックはあくまでも、実際に中に入っている具象オブジェクトの生成過程を意識せず、
その抽象化されたオブジェクトを扱う様にコーディングしておけば、例え生成過程が変わったとしても
メインロジックの修正は必要なくなってしまうわけです。あら便利。

このカテゴリーの先頭へ このページの先頭へ

■クラス図

ここで用いるサンプルのクラス図です。
準備中...

このカテゴリーの先頭へ このページの先頭へ

■サンプルの説明

サンプルの見た目
ComboBox を選択し、生産ボタンをクリックすると、ComboBox の選択値に応じて、
コントロールを作成し、画面に表示します。
サンプルのプロジェクトダウンロード

このカテゴリーの先頭へ このページの先頭へ

■コード

アプリケーションのエントリポイント StartUpForm です。
生産ボタンをクリックすると、ComboBox の選択値に応じて Factory(工場)を生成し、
Factory(工場)の生産したモノをForm に表示します。

StartUpForm.vb
Public Class StartUpForm
  Inherits System.Windows.Forms.Form

#Region " Windows フォーム デザイナで生成されたコード "

  Public Sub New()
    MyBase.New()

    ' この呼び出しは Windows フォーム デザイナで必要です。
    InitializeComponent()

    ' InitializeComponent() 呼び出しの後に初期化を追加します。
    AddHandler Application.ThreadException, AddressOf CommonExceptionAndHandler.ExceptionHandler.MyHandler
  End Sub

  ' Form は、コンポーネント一覧に後処理を実行するために dispose をオーバーライドします。
  Protected Overloads Overrides Sub Dispose(ByVal disposing As Boolean)
    If disposing Then
      If Not (components Is Nothing) Then
        components.Dispose()
      End If
    End If
    MyBase.Dispose(disposing)
  End Sub

  ' Windows フォーム デザイナで必要です。
  Private components As System.ComponentModel.IContainer

  ' メモ : 以下のプロシージャは、Windows フォーム デザイナで必要です。
  'Windows フォーム デザイナを使って変更してください。  
  ' コード エディタを使って変更しないでください。
  Friend WithEvents Label1 As System.Windows.Forms.Label
  Friend WithEvents ComboBox1 As System.Windows.Forms.ComboBox
  Friend WithEvents Button1 As System.Windows.Forms.Button
  Friend WithEvents Panel1 As System.Windows.Forms.Panel
  <System.Diagnostics.DebuggerStepThrough()> Private Sub InitializeComponent()
    Me.Label1 = New System.Windows.Forms.Label
    Me.ComboBox1 = New System.Windows.Forms.ComboBox
    Me.Button1 = New System.Windows.Forms.Button
    Me.Panel1 = New System.Windows.Forms.Panel
    Me.SuspendLayout()
    '
    'Label1
    '
    Me.Label1.Location = New System.Drawing.Point(8, 8)
    Me.Label1.Name = "Label1"
    Me.Label1.TabIndex = 0
    Me.Label1.Text = "Label1"
    '
    'ComboBox1
    '
    Me.ComboBox1.Location = New System.Drawing.Point(104, 8)
    Me.ComboBox1.Name = "ComboBox1"
    Me.ComboBox1.Size = New System.Drawing.Size(104, 20)
    Me.ComboBox1.TabIndex = 1
    Me.ComboBox1.Text = "ComboBox1"
    '
    'Button1
    '
    Me.Button1.Location = New System.Drawing.Point(208, 8)
    Me.Button1.Name = "Button1"
    Me.Button1.TabIndex = 4
    Me.Button1.Text = "Button1"
    '
    'Panel1
    '
    Me.Panel1.Location = New System.Drawing.Point(8, 40)
    Me.Panel1.Name = "Panel1"
    Me.Panel1.Size = New System.Drawing.Size(272, 216)
    Me.Panel1.TabIndex = 5
    '
    'StartUpForm
    '
    Me.AutoScaleBaseSize = New System.Drawing.Size(5, 12)
    Me.ClientSize = New System.Drawing.Size(292, 266)
    Me.Controls.Add(Me.Panel1)
    Me.Controls.Add(Me.Button1)
    Me.Controls.Add(Me.ComboBox1)
    Me.Controls.Add(Me.Label1)
    Me.Name = "StartUpForm"
    Me.Text = "Form1"
    Me.ResumeLayout(False)

  End Sub

#End Region

  '生産する種類
  Private Const KIND_COMBO As String = "ComboBox"
  Private Const KIND_LIST As String = "ListBox"
  '生産されるモノ(Control)に表示するデータ
  Private m_dt As DataTable

  '画面がロードされた時のイベント
  Private Sub StartUpForm_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.Load
    Me.Text = "Factory Method"
    Me.Label1.Text = "種類:"

    Me.ComboBox1.DropDownStyle = ComboBoxStyle.DropDownList
    Me.ComboBox1.Items.Clear()
    Me.ComboBox1.Items.Add(Me.KIND_COMBO)
    Me.ComboBox1.Items.Add(Me.KIND_LIST)
    Me.ComboBox1.SelectedIndex = 0

    Me.Button1.Text = "生産"

    '生産されるモノ(Control)に表示するデータ
    Me.m_dt = New DataTable("ゲーム機")
    Me.m_dt.Columns.Add("Name", GetType(String))
    Me.m_dt.Columns.Add("名前", GetType(String))
    Me.m_dt.Rows.Add(New String() {"FamilyComputer", "ファミコン"})
    Me.m_dt.Rows.Add(New String() {"DiskSystem", "ディスクシステム"})
    Me.m_dt.Rows.Add(New String() {"NintendoDS", "ニンテンドーDS"})
  End Sub

  '生産ボタンクリック時のイベント
  Private Sub Button1_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles Button1.Click
    Me.Panel1.Controls.Clear()
    Dim factory As AbstractListComponentFactory
    Dim listCtrl As IListComponent

    If Me.ComboBox1.Text = Me.KIND_COMBO Then
      factory = New ComboBoxFactory
    Else
      factory = New ListBoxFactory
    End If

    '生産
    listCtrl = factory.Create(Me.m_dt, "名前", "Name")
    Me.Panel1.Controls.Add(DirectCast(listCtrl, Control))
  End Sub
End Class
このカテゴリーの先頭へ このページの先頭へ

何かを生産する工場です。
外部(継承関係にないクラス)に公開するのは、Create メソッドのみで、
Create メソッド内で、実際の具象オブジェクトを生産するメソッド CreateProduct メソッドを呼んでいます。

AbstractListComponentFactory.vb
'何かのモノを生産する抽象クラス
Public MustInherit Class AbstractListComponentFactory

  '何かのモノを生産するメソッド
  Public Function Create(ByVal dt As DataTable, _
                 ByVal displayMember As String, _
                 ByVal valueMember As String) As IListComponent
    Dim p As IListComponent
    p = Me.CreateProduct(dt, displayMember, valueMember)
    Return p
  End Function

  '実際のモノを生産するメソッド定義
  Protected MustOverride Function CreateProduct(ByVal dt As DataTable, _
                                  ByVal displayMember As String, _
                                  ByVal valueMember As String) As IListComponent
End Class
このカテゴリーの先頭へ このページの先頭へ

ComboBox を生産する工場です。
AbstractListComponentFactory を継承しています。

ComboBoxFactory.vb
'ComboBox工場
Public Class ComboBoxFactory
  Inherits AbstractListComponentFactory

  '実際のモノ(ComboBox)を生産するメソッド実装
  Protected Overrides Function CreateProduct(ByVal dt As System.Data.DataTable, _
                               ByVal displayMember As String, _
                               ByVal valueMember As String) As IListComponent
    Dim myComboB As MyComboBox
    myComboB = New MyComboBox
    With myComboB
      .DataSource = dt
      .DisplayMember = displayMember
      .ValueMember = valueMember
      .Location = New System.Drawing.Point(10, 10)
      .Size = New System.Drawing.Size(240, 20)
    End With
    Return myComboB
  End Function
End Class
このカテゴリーの先頭へ このページの先頭へ

ListBox を生産する工場です。
AbstractListComponentFactory を継承しています。

ListBoxFactory.vb
'ListBox工場
Public Class ListBoxFactory
  Inherits AbstractListComponentFactory

  '実際のモノ(ListBox)を生産するメソッド実装
  Protected Overrides Function CreateProduct(ByVal dt As System.Data.DataTable, _
                               ByVal displayMember As String, _
                               ByVal valueMember As String) As IListComponent
    Dim myListB As MyListBox
    myListB = New MyListBox
    With myListB
      .DataSource = dt
      .DisplayMember = displayMember
      .ValueMember = valueMember
      .Location = New System.Drawing.Point(10, 10)
      .Size = New System.Drawing.Size(240, 150)
    End With
    Return myListB
  End Function
End Class
このカテゴリーの先頭へ このページの先頭へ

Factory(工場)によって生産される何かを表すインターフェースです。

IListComponent.vb
'Factoryによって生産される何か
Public Interface IListComponent
  Inherits System.ComponentModel.IComponent
End Interface
このカテゴリーの先頭へ このページの先頭へ

Factory(工場)によって生産される何かの 1 つ、ComboBox です。
IListComponent インターフェースを implement しています。

MyComboBox.vb
'ComboBox
Public Class MyComboBox
  Inherits ComboBox
  Implements IListComponent
End Class
このカテゴリーの先頭へ このページの先頭へ

Factory(工場)によって生産される何かの 1 つ、ListBox です。
IListComponent インターフェースを implement しています。

MyListBox.vb
'ListBox
Public Class MyListBox
  Inherits ListBox
  Implements IListComponent
End Class
このカテゴリーの先頭へ このページの先頭へ

■ひとこと

Templete Method パターン同様、比較的業務で良く使われるデザインパターンです。
ComboBox や ListBox を new した後に、Factory(工場)で色々やってから、メインロジックに渡してくれます。
もし、「携帯ゲーム機については"(携帯)"という文字をおしりに付加して表示する」という仕様変更があったとします。
そして、画面で生成している、DataTable オブジェクトの中身は変えないで欲しいという依頼があったとしたら。
具象の Factory を修正するか、もしくは親クラスの中で 別の DataTable を用意してやる等、
メインロジック以外の箇所で修正が終わってしまうはずです。


似たような名前のデザインパターンに、 Abstract Factory パターンがありますが、
微妙に色々違ってたりします。ちょっと見てみたりして下さい。

このカテゴリーの先頭へ このページの先頭へ