Template Method (テンプレートメソッド) パターン
各クラス間の共通した処理の流れを親クラスで定義して、子クラスがそれぞれの処理中身を実装するというデザインパターンです。
時と場合によっては、型にはめられた方がコーディングしやすいって事もあるでしょう。
また、決まりきった処理の流れを一々それぞれのクラスに実装するのは、ちょっと格好悪いですよね。
サンプルではWindowsアプリケーションですが、Webアプリケーションなんかだとこんな事を各画面に実装してたりしませんか?
1. セッションにアカウント情報のオブジェクトが入ってなければ、エラー画面に遷移する。
2. Request オブジェクトからパラメータを取得して、変数に入れる。
3. 2 で取ってきた変数を使って、何かの業務ロジックを実行する。
4. 次の画面に遷移する。
上記 1 〜 4 までの処理の流れが各画面に実装されていたとして、1 の処理の後に「セッションに何かのオブジェクトが入ってないければ、エラー画面に遷移する」
という仕様の追加があったとしましょう。 そして沢山画面があったとしたら...恐ろしい。格好悪いでは済まされない...
Template Method パターンで実装しておけば、このような仕様追加もなんてことないです。
■クラス図
ここで用いるサンプルのクラス図です。
準備中...
■サンプルの説明

一番左にある画面の ComboBox を選択すると、選択値に応じて真ん中か右の画面を表示します。
ここでは、真ん中と右の画面は、「DataGrid になにかのデータを表示して、それぞれ何かのメッセージを出す」
という仕様であるとします。
サンプルのプロジェクトダウンロード
■コード
アプリケーションのエントリポイント StartUpForm です。
ComboBox の選択インデックスが変わったとき、サンプルの説明にある、
真ん中か、右の画面をモーダル表示します。
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 にバインドするか、何をメッセージ表示するかは
子クラスに任せています。
'抽象基底クラス 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が呼ばれました」と表示します。
'基底クラスを継承した子画面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が呼ばれました」と表示します。
'基底クラスを継承した子画面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 パターンは比較的、実務でよく使うデザインパターンですので、覚えておいて損は無いです。