Template Method (テンプレートメソッド) パターン

各クラス間の共通した処理の流れを親クラスで定義して、子クラスがそれぞれの処理中身を実装するというデザインパターンです。
時と場合によっては、型にはめられた方がコーディングしやすいって事もあるでしょう。
また、決まりきった処理の流れを一々それぞれのクラスに実装するのは、ちょっと格好悪いですよね。
サンプルではWindowsアプリケーションですが、Webアプリケーションなんかだとこんな事を各画面に実装してたりしませんか?
1. セッションにアカウント情報のオブジェクトが入ってなければ、エラー画面に遷移する。
2. Request オブジェクトからパラメータを取得して、変数に入れる。
3. 2 で取ってきた変数を使って、何かの業務ロジックを実行する。
4. 次の画面に遷移する。


上記 1 〜 4 までの処理の流れが各画面に実装されていたとして、1 の処理の後に「セッションに何かのオブジェクトが入ってないければ、エラー画面に遷移する」
という仕様の追加があったとしましょう。 そして沢山画面があったとしたら...恐ろしい。格好悪いでは済まされない...
Template Method パターンで実装しておけば、このような仕様追加もなんてことないです。

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

■クラス図

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

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

■サンプルの説明

サンプルの見た目1  サンプルの見た目2  サンプルの見た目3
一番左にある画面の ComboBox を選択すると、選択値に応じて真ん中か右の画面を表示します。
ここでは、真ん中と右の画面は、「DataGrid になにかのデータを表示して、それぞれ何かのメッセージを出す」
という仕様であるとします。
サンプルのプロジェクトダウンロード

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

■コード

アプリケーションのエントリポイント StartUpForm です。
ComboBox の選択インデックスが変わったとき、サンプルの説明にある、
真ん中か、右の画面をモーダル表示します。

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 ComboBox1 As System.Windows.Forms.ComboBox
  <System.Diagnostics.DebuggerStepThrough()> Private Sub InitializeComponent()
    Me.ComboBox1 = New System.Windows.Forms.ComboBox
    Me.SuspendLayout()
    '
    'ComboBox1
    '
    Me.ComboBox1.Location = New System.Drawing.Point(80, 104)
    Me.ComboBox1.Name = "ComboBox1"
    Me.ComboBox1.Size = New System.Drawing.Size(121, 20)
    Me.ComboBox1.TabIndex = 0
    Me.ComboBox1.Text = "ComboBox1"
    '
    'StartUpForm
    '
    Me.AutoScaleBaseSize = New System.Drawing.Size(5, 12)
    Me.ClientSize = New System.Drawing.Size(292, 266)
    Me.Controls.Add(Me.ComboBox1)
    Me.Name = "StartUpForm"
    Me.Text = "StartUpForm"
    Me.ResumeLayout(False)

  End Sub

#End Region

  Private Const PROGRAM_DATA_FORM As String = "プログラムで作成したデータのフォーム"
  Private Const ACCESS_DATA_FORM As String = "Accessのデータのフォーム"

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

    Me.ComboBox1.Size = New Size(192, 20)
    Me.ComboBox1.DropDownStyle = ComboBoxStyle.DropDownList
    Me.ComboBox1.Items.Clear()
    Me.ComboBox1.Items.Add(Me.PROGRAM_DATA_FORM)
    Me.ComboBox1.Items.Add(Me.ACCESS_DATA_FORM)
  End Sub

  'ComboBoxの選択インデックスが変更された時のイベント
  Private Sub ComboBox1_SelectedIndexChanged(ByVal sender As Object, ByVal e As System.EventArgs) _
    Handles ComboBox1.SelectedIndexChanged
    Dim showForm As AbstractBaseForm
    Select Case Me.ComboBox1.Text
      Case Me.PROGRAM_DATA_FORM : showForm = New ChildForm1
      Case Me.ACCESS_DATA_FORM : showForm = New ChildForm2
    End Select
    showForm.ShowDialog(Me)
    showForm.Dispose()
  End Sub
End Class
このカテゴリーの先頭へ このページの先頭へ

処理の流れを定義している親クラスの AbstractBaseForm クラスです。
このクラスは、何かのデータをバインドした DataGrid コントロールの描画と、
何かのメッセージを表示する事を行います。
具体的に何を DataGrid にバインドするか、何をメッセージ表示するかは
子クラスに任せています。

AbstractBaseForm.vb
'抽象基底クラス
Public MustInherit Class AbstractBaseForm
  Inherits System.Windows.Forms.Form

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

  Public Sub New()
    MyBase.New()

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

    ' InitializeComponent() 呼び出しの後に初期化を追加します。

  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 フォーム デザイナを使って変更してください。  
  ' コード エディタを使って変更しないでください。
  <System.Diagnostics.DebuggerStepThrough()> Private Sub InitializeComponent()
    '
    'BaseForm
    '
    Me.AutoScaleBaseSize = New System.Drawing.Size(5, 12)
    Me.ClientSize = New System.Drawing.Size(292, 266)
    Me.Name = "BaseForm"
    Me.Text = "Form1"

  End Sub

#End Region

  '画面がロードされた時のイベント
  Private Sub AbstractBaseForm_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.Load
    If Not DesignMode Then
      Me.Text = "TemplateMethod"

      'DataTableを取得する
      Dim dt As DataTable
      dt = Me.GetDisplayData()

      '表示している表の名前を表示するラベルを描画する
      Dim titleLabel As Label = New Label
      titleLabel.Location = New System.Drawing.Point(0, 0)
      titleLabel.Dock = DockStyle.Top
      titleLabel.TextAlign = ContentAlignment.MiddleCenter
      '先に取得したDataTableのTableNameプロパティを表示
      titleLabel.Text = dt.TableName
      Me.Controls.Add(titleLabel)

      'DataTableをバインドして表示するDataGridを描画する
      Dim grid As DataGrid = New DataGrid
      grid.Location = New System.Drawing.Point(10, 25)
      grid.Size = New System.Drawing.Size(200, 200)
      grid.DataSource = dt
      Me.Controls.Add(grid)

      '画面ロード時に表示するメッセージを取得し、
      'メッセージ表示する
      Dim displayMessage As String = Me.GetDisplayMessage()
      MessageBox.Show(displayMessage, _
              Me.Text, _
              MessageBoxButtons.OK, _
              MessageBoxIcon.Information)
    End If
  End Sub

  'データグリッドに表示するデータを取得するメソッド定義
  Protected MustOverride Function GetDisplayData() As DataTable
  '画面ロード時に表示するメッセージ取得メソッド定義
  Protected MustOverride Function GetDisplayMessage() As String

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

AbstractBaseForm クラスを継承した、1 つ目の子クラスの、ChildForm1 クラスです。
実行時に DataGrid にバインドするデータを、プログラム内で生成します。
実行時に表示されるメッセージを「子1が呼ばれました」と表示します。

ChildForm1.vb
'基底クラスを継承した子画面1
Public Class ChildForm1
  Inherits AbstractBaseForm

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

  Public Sub New()
    MyBase.New()

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

    ' InitializeComponent() 呼び出しの後に初期化を追加します。

  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 フォーム デザイナを使って変更してください。  
  ' コード エディタを使って変更しないでください。
  <System.Diagnostics.DebuggerStepThrough()> Private Sub InitializeComponent()
    components = New System.ComponentModel.Container
  End Sub

#End Region

  '基底クラスのデータグリッドに表示するデータを取得するメソッド実装
  Protected Overrides Function GetDisplayData() As System.Data.DataTable
    Dim dt As DataTable = New DataTable("コードで生成")
    dt.Columns.Add("ID", GetType(String))
    dt.Columns.Add("Name", GetType(String))

    dt.Rows.Add(New String() {"1", "中博俊 さん"})
    dt.Rows.Add(New String() {"2", "じゃんぬ さん"})
    dt.Rows.Add(New String() {"3", "Jitta さん"})
    dt.Rows.Add(New String() {"4", "trapemiya さん"})
    dt.Rows.Add(New String() {"5", "やねうらお さん"})
    dt.Rows.Add(New String() {"6", "まゆりん さん"})
    dt.Rows.Add(New String() {"7", "なおこ(・∀・)"})
    Return dt
  End Function

  '基底クラスの画面ロード時に表示するメッセージ取得メソッド実装
  Protected Overrides Function GetDisplayMessage() As String
    Return "子1が呼ばれました"
  End Function
End Class
このカテゴリーの先頭へ このページの先頭へ

AbstractBaseForm クラスを継承した、2 つ目の子クラスの、ChildForm2 クラスです。
実行時に DataGrid にバインドするデータを、Accessデータベースから取得します。
実行時に表示されるメッセージを「子2が呼ばれました」と表示します。

ChildForm2.vb
'基底クラスを継承した子画面2
Public Class ChildForm1
  Inherits AbstractBaseForm

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

  Public Sub New()
    MyBase.New()

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

    ' InitializeComponent() 呼び出しの後に初期化を追加します。

  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 フォーム デザイナを使って変更してください。  
  ' コード エディタを使って変更しないでください。
  <System.Diagnostics.DebuggerStepThrough()> Private Sub InitializeComponent()
    components = New System.ComponentModel.Container
  End Sub

#End Region

  'SELECT文
  Private Const SELECT_SCRIPT As String = "SELECT ID, Name FROM wankuma"

  '基底クラスのデータグリッドに表示するデータを取得するメソッド実装
  Protected Overrides Function GetDisplayData() As System.Data.DataTable
    Dim mdbPath As String = System.IO.Path.Combine(Application.StartupPath, "TemplateMethod.mdb")
    Dim connection_String As String = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" + mdbPath + ";User Id=admin;Password=;"

    'Accessよりデータの取得
    Dim myDa As CommonDataAccess.AbstructDataAccess
    myDa = New CommonDataAccess.OleDataAccess
    Try
      myDa.Open(connection_String)
      Dim dt As DataTable = myDa.DataSelect(Me.SELECT_SCRIPT)
      dt.TableName = "Access から取得"
      Return dt
    Finally
      myDa.Close()
    End Try
  End Function

  '基底クラスの画面ロード時に表示するメッセージ取得メソッド実装
  Protected Overrides Function GetDisplayMessage() As String
    Return "子2が呼ばれました"
  End Function
End Class
このカテゴリーの先頭へ このページの先頭へ

■ひとこと

ChildForm1 クラスと ChildForm2 クラスをご覧にいただければ
お解りだとは思いますが、1 個抽象の親を作ってしまってからは、これに更に似たような画面を追加しても
基本的には、GetDisplayData メソッドと、GetDisplayMessage メソッドだけ実装すればよいのです。


更に、サンプルでは DataGrid コントロールを使用して、画面に描画を行っていましたが、
DataGrid コントロールではなく、ListView コントロールを使って描画するように仕様変更があったとしても、
AbstractBaseForm クラスの Load イベントのコードだけ修正すればお終いです。
Template Method パターンは比較的、実務でよく使うデザインパターンですので、覚えておいて損は無いです。

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