デザインパターン勉強会③Template Method

<h1>はじめに</h1>
ZOOM勉強会の議事録です。
第3回は、Template Methodパターンです。
<a href="https://www.amazon.co.jp/%E5%A2%97%E8%A3%9C%E6%94%B9%E8%A8%82%E7%89%88Java%E8%A8%80%E8%AA%9E%E3%81%A7%E5%AD%A6%E3%81%B6%E3%83%87%E3%82%B6%E3%82%A4%E3%83%B3%E3%83%91%E3%82%BF%E3%83%BC%E3%83%B3%E5%85%A5%E9%96%80-%E7%B5%90%E5%9F%8E-%E6%B5%A9/dp/4797327030">「増補改訂版Java言語で学ぶデザインパターン入門」</a>を読んで、プログラム作成・パターンの理解を行います。

<h1>Template Methodとは</h1>
スーパークラスで処理の枠組みを定め、サブクラスで具体的処理を定めるパターンです。

役割は以下です。

<ul><li><h4>AbstractClass</h4></li>枠組みとなるテンプレートメソッドを実装する抽象クラス<li><h4>ConcreteClass</h4></li>抽象メソッドを具体的に実装するクラス</ul>

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1585226/33b7ac9d-de94-363f-8e22-1ab28cf8b20f.png)


<h2>AbstractClass</h2>
枠組みを定めるスーパークラスです。
抽象クラスに処理の流れを定義するテンプレートメソッドとその中で呼び出す抽象メソッドを定義します。

```vb:AbstractDisplay
    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
```
テンプレートメソッドで定義した流れは以下のとおりです。
<ul><li>`Open()`を呼ぶ</li><li>`Print()`を5回呼ぶ</li><li>`Close()`を呼ぶ</li></ul>
AbstractClassでは、それぞれのメソッドで何が行われるかを、まだ定義していません。

<h2>ConcreteClass</h2>
AbstractClassを継承し抽象メソッドに肉付けをしていきます。
`CharDisplay`と`StringDisplay`が今回のConcreteClassです。

```vb:CharDisplay
    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`です。
 
```vb: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()`をオーバーライドして具体的な処理を肉付けしています。

<h2>呼び出し側</h2>
インスタンス化してテンプレートメソッドの`Display()`を呼ぶと実行結果は以下のようになります。

```vb:Main
    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()`の重複する内容を記述しなくてよくなるため、修正時の修正箇所を減らすことができます。
<h1>余談</h1>
Template Methodは比較的理解しやすかったため、業務に導入できそうな場所がいくつか挙がりました。<br>
一方で、処理が同じだからと言って何でも共通化すると、余計な修正が必要になったりします。
そこからどのようにデザインパターンを取り入れていくのがよいかの話になり、<a href="https://fukabori.fm">Fukabori.fm</a>の49回が参考になるのでは？と意見がでました。
この中では、デザインパターンの使い方について以下のように言っていました。
<ul><li>デザインパターンは最初から設計するものでなくリファクタリングの結果生まれてくるもの</li><li>リファクタリングのゴール・ターゲットとしてのデザインパターン</li><li>リファクタリングに方針を与えるものがデザインパターン</li></ul>

時代を追うごとにデザインパターンの使い方が変わっていき、今ではリファクタリングに方針を与えるものとして位置づけられているというような話でした。
非常に面白い話でしたので、ぜひ聞いてみてください。


<h1>まとめ</h1>
今回はTemplate Methodパターンについて学びました。
抽象クラスの強みを生かしたパターンなのかなと思います。
普段あまり使っていなかった抽象クラスにも良い活用法があると知れた良い回でした。