Template Methodパターンを使うメリットとPHP実装例

## Template Methodパターンとは

Template Methodパターンは、一連の処理の大まかなアルゴリズムをあらかじめ定義しておき、具体的な実装はサブクラスに任せることを目的としたデザインパターンです。フレームワークを構築するための手段としてよく利用されるそうです。

最初は普通の抽象メソッドとの違いがよくわかりませんでしたが、実装例を見て疑問が解消しました。

下記のdisplay()がTemplate Methodと呼ばれるものです。

```php
abstract class AbstractDisplay
{
    /**
     * オープン => 5回プリント => クローズ
     * という大まかな流れだけが決まっている
     */
    public function display(): void
    {
        $this->open();
        for ($i = 0; $i < 5; $i++) {
            $this->print();
        }
        $this->close();
    }

    public abstract function open(): void;
    public abstract function print(): void;
    public abstract function close(): void;
}
```

このようにアルゴリズムの大枠だけを抽象クラスで定義しておき、具体的な実装はサブクラスに委ねるのがTemplate Methodパターンです。このAbstractDisplayのサブクラスは、`open()`、`print()`、`close()`という3つのメソッドを実装しなければなりません。これをサブクラスの責任（Subclass Responsibility）というそうです。

### サブクラスの実装例

Template Methodを利用したサブクラスの実装の具体例です。

さきほどの抽象クラスAbstractDisplayを継承して、サブクラスCharDisplayとStringDisplayを実装します。

CharDisplay.php

```php
class CharDisplay extends AbstractDisplay
{
    private $char;

    public function __construct(string $char)
    {
        $this->char = $char;
    }

    public function open(): void
    {
        echo '<<';
    }

    public function print(): void
    {
        echo $this->char;
    }

    public function close(): void
    {
        echo '>>' . "\n";
    }
}
```

StringDisplay.php

```php
class StringDisplay extends AbstractDisplay
{
    private $string;
    private $width;

    public function __construct(string $string)
    {
        $this->string = $string;
        $this->width = mb_strlen($string);
    }

    public function open(): void
    {
        echo $this->printLine();
    }

    public function print(): void
    {
        echo '|' . $this->string . '|' . "\n";
    }

    public function close(): void
    {
        echo $this->printLine();
    }

    private function printLine(): void
    {
        echo '+' . str_repeat('-', $this->width) . '+' . "\n";
    }
}
```

2つのサブクラスは、どちらもAbstractDisplayクラスを継承しています。

クラス図で表すと次のようになります。
<img width="525" alt="スクリーンショット 2020-08-09 20.45.16.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/500854/4b68ad52-fba2-f6a1-448a-3725563896ea.png">



「オープン => 5回プリント => クローズ」という流れは共通ですが、具体的な実装はサブクラスによって違います。実際にそれぞれのサブクラスをインスタンス化して、display()を実行した結果は次のようになります。

```php
function main()
{
    $charDisplay = new CharDisplay('H');
    $charDisplay->display();

    $stringDisplay = new StringDisplay('Hello, World');
    $stringDisplay->display();
}
```

```bash
-> % php TemplateMethod/Main.php
<<HHHHH>>
+------------+
|Hello, World|
|Hello, World|
|Hello, World|
|Hello, World|
|Hello, World|
+------------+
```

### アルゴリズムを一元管理することができる

Template Methodパターンを利用する利点は、アルゴリズムを共通化できることだそうです。

似たような処理を複数のクラスに実装しなければならない場合、Template Methodを定義することで、アルゴリズムの共通部分を一箇所にまとめることができます。それにより、何度も似たようなロジックを記述する手間が省けるだけでなく、アルゴリズムに変更を加えたい場合も修正箇所が一箇所で済むようになります。

### Template Methodパターンのメリット

Template Methodパターンは、一連の処理のおおまかなアルゴリズムをあらかじめ決めておき、具体的な実装はサブクラスに委ねるデザインパターンでした。テンプレートメソッドの中に、インスタンス生成のためのメソッドを含むものはFactory Methodパターンと呼ばれます。このように応用の幅が効くデザインパターンのようです。

Template Methodパターンのメリットは次の通りです。

* 共通するアルゴリズムを使い回すことができる
* サブクラスの実装が簡潔になる
* アルゴリズムを修正する際はテンプレートメソッドの修正だけで済む

Template Methodパターンの実装例はこちらです。

```php
<?php

abstract class AbstractDisplay
{
    /**
     * オープン => 5回プリント => クローズ
     * という大まかな流れだけは決まっている
     */
    public function display(): void
    {
        $this->open();
        for ($i = 0; $i < 5; $i++) {
            $this->print();
        }
        $this->close();
    }

    public abstract function open(): void;
    public abstract function print(): void;
    public abstract function close(): void;
}

class CharDisplay extends AbstractDisplay
{
    private $char;

    public function __construct(string $char)
    {
        $this->char = $char;
    }

    public function open(): void
    {
        echo '<<';
    }

    public function print(): void
    {
        echo $this->char;
    }

    public function close(): void
    {
        echo '>>' . "\n";
    }
}

class StringDisplay extends AbstractDisplay
{
    private $string;
    private $width;

    public function __construct(string $string)
    {
        $this->string = $string;
        $this->width = mb_strlen($string);
    }

    public function open(): void
    {
        echo $this->printLine();
    }

    public function print(): void
    {
        echo '|' . $this->string . '|' . "\n";
    }

    public function close(): void
    {
        echo $this->printLine();
    }

    private function printLine(): void
    {
        echo '+' . str_repeat('-', $this->width) . '+' . "\n";
    }
}

function main()
{
    $charDisplay = new CharDisplay('H');
    $charDisplay->display();

    $stringDisplay = new StringDisplay('Hello, World');
    $stringDisplay->display();
}

main();
```