RitoLabo

TemplateMethodパターン | PHPデザインパターン

  • 公開:
  • カテゴリ: PHP DesignPatterns
  • タグ: PHP,Behavior,DesignPatterns,TemplateMethod

TemplateMethodパターンは、スーパー(基底)クラスとして定義されたメソッドをサブクラスで継承し、1つの処理モデルを構築するパターンです。

TemplateMethodパターンクラス図

パターン名からきている通り、テンプレート=共通の処理を定義したスーパークラスをそれぞれのサブクラスたちが継承し、継承元から与えられたそれぞれの機能を実装し、さらにそのクラス独自の処理を実装することで、それぞれが1つの完成形となります。

実装例

例として車で考えてみます。

車にはたくさんの車種がありますが、「ハンドル(左右・クラクション)」「アクセル(進む)」「ブレーキ(止まる)」の機能に関してはどの車も持っている共通のものです。

また、「エンジン」「ボディ」「内装」というのももちろん全ての車にあるものですが、これらは車種によってスペックや材質が違ったりします。

そして、車種によって、その車にしかない装備や機能などもあったありします。

これらをTemplateMethodパターンで実装してみます。

スーパー(基底)クラス

まずはスーパークラスです。ここには、全車種に共通の機能や、車種によって中身が違うけれど、共通のものとして持っておくべき機能を定義します。

<?php

abstract class Car
{
/**
* ハンドル
* @return array
*/
public function handle()
{
return ['right', 'left', 'horn'];
}

/**
* アクセル
* @return string
*/
public function accelerator()
{
return 'move on!';
}

/**
* ブレーキ
* @return string
*/
public function brake()
{
return 'stop';
}

// タイプ
protected abstract function type();
// エンジン
protected abstract function engine();
// ボディ
protected abstract function body();
// 内装
protected abstract function interior();
}

ハンドル・アクセス・ブレーキに関しては、全車種で同じ機能なので、ここで処理を定義しています。

一方、共通で持っている機能だけれど、車種によって違うものはサブクラスで定義するものとしてメソッドの宣言のみを行っています。

ちなみに、abstractで宣言されたメソッドは、継承先であるサブクラスでは必ず実装しなければいけないメソッドになります。(実装しなければエラーになります)

これで車の基底クラスが出来ました。

サブクラス

次に、これらを継承してサブクラスを定義していきます。

<?php

class MyCarNomal extends Car
{
public function type()
{
return 'ノーマル';
}

public function engine()
{
return '直列4気筒DOHC';
}

public function body()
{
return 'Basic';
}

public function interior()
{
return 'white';
}
}

class MyCarSports extends Car
{
public function type()
{
return 'スポーツ';
}

public function engine()
{
return 'V8ターボ';
}

public function body()
{
return 'carbon';
}

public function interior()
{
return 'red';
}

public function wing()
{
return 'GTウイング';
}
}

class MyCarLuxury extends Car
{
public function type()
{
return 'ラグジュアリー';
}

public function engine()
{
return '3.0L直列6気筒ターボ';
}

public function body()
{
return 'Fiber reinforced plastic'; // 繊維強化プラスチック
}

public function interior()
{
return 'black';
}

public function sunroof()
{
return 'サンルーフ';
}
}

ここでは、3つのサブクラスを定義しています。それぞれ「ノーマルカー」「スポーツカー」「ラグジュアリーカー」の3つです。

最初に定義したスーパークラスをそれぞれのサブクラスが継承(extends)しています。

サブクラスでは、基底クラスで宣言したメソッドをそれぞれのサブクラスで定義し、それ以外に、各々のクラス独自の機能(メソッド)を実装しています。

動作確認

これで一連の実装が完了したので、インスタンス化を行い、出力を行います。

<?php

abstract class Car
{
/**
* ハンドル
* @return array
*/
public function handle()
{
return ['right', 'left', 'horn'];
}

/**
* アクセル
* @return string
*/
public function accelerator()
{
return 'move on!';
}

/**
* ブレーキ
* @return string
*/
public function brake()
{
return 'stop';
}

// タイプ
protected abstract function type();
// エンジン
protected abstract function engine();
// ボディ
protected abstract function body();
// 内装
protected abstract function interior();
}

class MyCarNomal extends Car
{
public function type()
{
return 'ノーマル';
}

public function engine()
{
return '直列4気筒DOHC';
}

public function body()
{
return 'Basic';
}

public function interior()
{
return 'white';
}
}

class MyCarSports extends Car
{
public function type()
{
return 'スポーツ';
}

public function engine()
{
return 'V8ターボ';
}

public function body()
{
return 'carbon';
}

public function interior()
{
return 'red';
}

public function wing()
{
return 'GTウイング';
}
}

class MyCarLuxury extends Car
{
public function type()
{
return 'ラグジュアリー';
}

public function engine()
{
return '3.0L直列6気筒ターボ';
}

public function body()
{
return 'Fiber reinforced plastic'; // 繊維強化プラスチック
}

public function interior()
{
return 'black';
}

public function sunroof()
{
return 'サンルーフ';
}
}


$normal = new MyCarNomal();
echo "タイプ:" . $normal->type() . "<br>";
echo "エンジン:" . $normal->engine() . "<br>";
echo "アクセル:" . $normal->accelerator() . "<br><br>";

$sports = new MyCarSports();
echo "タイプ:" . $sports->type() . "<br>";
echo "エンジン:" . $sports->engine() . "<br>";
echo "アクセル:" . $sports->accelerator() . "<br>";
echo "独自装備:" . $sports->wing() . "<br><br>";

$luxury = new MyCarLuxury();
echo "タイプ:" . $luxury->type() . "<br>";
echo "エンジン:" . $luxury->engine() . "<br>";
echo "アクセル:" . $luxury->accelerator() . "<br>";
echo "独自装備:" . $luxury->sunroof() . "<br><br>";

実際にブラウザで確認すると、以下のように表示されます。

ブラウザからTemplateMethodパターンの出力表示

  • スーパークラスで定義した、共通機能である「アクセル」
  • スーパークラスで宣言しサブクラスで定義した、共通要素である「タイプ」「エンジン」
  • サブクラスで定義した、独自要素である「独自機能」

TemplateMethodパターンによって、これらの処理を実装できました。

まとめ

TemplateMethodパターンを用いれば、共通処理をまとめる事ができ、それを使ってサブクラスで複数の処理モデルを作成する事が出来ます。機能の変更や追加の際も最少の記述で済むので、効率良く開発を進めていく事が出来ます。

LaravelやCakePHPなどのPHPフレームワークでも、自身で作成するコントローラにはAppControllerが継承されていたりと、結構目に触れるところでも使われているので見てみてください。