はじめに
ZOOM勉強会の議事録です。 第3回は、Template Methodパターンです。 「増補改訂版Java言語で学ぶデザインパターン入門」を読んで、プログラム作成・パターンの理解を行います。Template Methodとは
スーパークラスで処理の枠組みを定め、サブクラスで具体的処理を定めるパターンです。役割は以下です。
AbstractClass
枠組みを定めるスーパークラスです。 抽象クラスに処理の流れを定義するテンプレートメソッドとその中で呼び出す抽象メソッドを定義します。 Public MustInherit Class AbstractDisplay
''' <summary>
''' 抽象メソッド:開く
''' </summary>
Protected MustOverride Sub Open()
''' <summary>
''' 抽象メソッド:書く
''' </summary>
Protected MustOverride Sub Print()
''' <summary>
''' 抽象メソッド:閉じる
''' </summary>
Protected MustOverride Sub Close()
''' <summary>
''' 表示:メソッドの呼出し順を設定するが、メソッドの中身は定めない
''' </summary>
Public Sub Display()
'1.--<< 開いて >>--
Open()
'2.--<< 書いて >>--
For i As Integer = 0 To 4
Print()
Next
'3.--<< 閉じる >>--
Close()
End Sub
End Class
テンプレートメソッドで定義した流れは以下のとおりです。
- `Open()`を呼ぶ
- `Print()`を5回呼ぶ
- `Close()`を呼ぶ
ConcreteClass
AbstractClassを継承し抽象メソッドに肉付けをしていきます。 `CharDisplay`と`StringDisplay`が今回のConcreteClassです。 Public Class CharDisplay : Inherits AbstractDisplay
Private ch As Char
''' <summary>
''' コンストラクタ:表示する値を受け取る
''' </summary>
''' <param name="ch"></param>
Public Sub New(ByVal ch As Char)
Me.ch = ch
End Sub
'******** 以下、抽象メソッドへの肉付け **********
''' <summary>
''' 開く
''' </summary>
Protected Overrides Sub Open()
Console.Write("<<")
End Sub
''' <summary>
''' 書く
''' </summary>
Protected Overrides Sub Print()
Console.Write(ch.ToString)
End Sub
''' <summary>
''' 閉じる
''' </summary>
Protected Overrides Sub Close()
Console.WriteLine(">>")
Console.ReadLine()
End Sub
End Class
以下がもう一つのConcreteClassのStringDisplay
です。
Public Class StringDisplay : Inherits AbstractDisplay
Private str As String
Private width As Integer
''' <summary>
''' コンストラクタ:表示する値を取得する
''' </summary>
''' <param name="str"></param>
Public Sub New(ByVal str As String)
Me.str = str
Me.width = str.Length
End Sub
'******** 以下、抽象メソッドへの肉付け **********
''' <summary>
''' 開く
''' </summary>
Protected Overrides Sub Open()
PrintLine()
End Sub
''' <summary>
''' 書く
''' </summary>
Protected Overrides Sub Print()
Console.WriteLine("|" & str & "|")
End Sub
''' <summary>
''' 閉じる
''' </summary>
Protected Overrides Sub Close()
PrintLine()
Console.ReadLine()
End Sub
''' <summary>
''' 区切り文字を一列表示する
''' </summary>
Private Sub PrintLine()
Console.Write("*")
For i As Integer = 1 To width
Console.Write("-")
Next
Console.WriteLine("*")
End Sub
End Class
CharDisplay
とStringDisplay
では、抽象メソッドとして定義していたOpen()``Print()``Close()
をオーバーライドして具体的な処理を肉付けしています。
呼び出し側
インスタンス化してテンプレートメソッドの`Display()`を呼ぶと実行結果は以下のようになります。 Sub Main()
Dim cd = New CharDisplay("c"c)
cd.Display()
'CharDisplay出力結果
'<<ccccc>>
Dim sd = New StringDisplay("Hello")
sd.Display()
'StringDisplay出力結果
'*-----*
'|Hello|
'|Hello|
'|Hello|
'|Hello|
'|Hello|
'*-----*
End Sub
以上のようにCharDisplay
とStringDisplay
は、同じDisplay()
を呼んでいるのですが、肉付けが異なるため出力結果が異なります。
このようにメソッドの内容をざっくりと決めて詳細はクラスごとに変えることができます。
Template Methodを使用すると全体の流れが変わったときには、AbstractClassのみを変更すればよく、新しく同じような流れの処理を追加したいときには、ConcreteClassを追加すればよいといった形になります。
各具象クラスにDisplay()
の重複する内容を記述しなくてよくなるため、修正時の修正箇所を減らすことができます。
余談
Template Methodは比較的理解しやすかったため、業務に導入できそうな場所がいくつか挙がりました。一方で、処理が同じだからと言って何でも共通化すると、余計な修正が必要になったりします。 そこからどのようにデザインパターンを取り入れていくのがよいかの話になり、Fukabori.fmの49回が参考になるのでは?と意見がでました。 この中では、デザインパターンの使い方について以下のように言っていました。
- デザインパターンは最初から設計するものでなくリファクタリングの結果生まれてくるもの
- リファクタリングのゴール・ターゲットとしてのデザインパターン
- リファクタリングに方針を与えるものがデザインパターン
時代を追うごとにデザインパターンの使い方が変わっていき、今ではリファクタリングに方針を与えるものとして位置づけられているというような話でした。
非常に面白い話でしたので、ぜひ聞いてみてください。