会員ランクごとに画面イメージを切り替えよう
会員のランクによって画面(Web)のイメージを変えたいと思いました。
さっそく書いてみましょう。
class Template
{
const Bronze = 1;
const Silver = 2;
const Gold = 3;
private int $rank;
public function setRank(int $rank)
{
$this->rank = $rank;
}
public function view()
{
switch($this->rank) {
case Template::Bronze:
echo 'Bronzeランクヘッダー' . "\n";
echo 'Bronzeランクボディ' . "\n";
echo '共通フッター' . "\n";
break;
case Template::Silver:
echo 'Silverランクヘッダー' . "\n";
echo 'Silverランクボディ' . "\n";
echo '共通フッター' . "\n";
break;
case Template::Gold:
echo 'Goldランクヘッダー' . "\n";
echo 'Goldランクボディ' . "\n";
echo '共通フッター' . "\n";
break;
}
}
}
$template = new Template();
$template->setRank(Template::Gold);
$template->view();
例ではシンプルですが、これがHTMLだとすると非常に強大なswitch分になってしまいますね。
では、Template Methodパターンでの実装を見てみましょう。
Template Methodパターンとは
抽象クラスで処理の流れを定義し、継承した具象クラスで処理の中身を実装するパターンです。
abstract class AbstractTemplate
{
public abstract function header(): void;
public abstract function body(): void;
public abstract function footer(): void;
public function view(): void
{
$this->header();
$this->body();
$this->footer();
}
}
class BronzeRankTemplate extends AbstractTemplate
{
public function header(): void
{
echo 'Bronzeランクヘッダー' . "\n";
}
public function body(): void
{
echo 'Bronzeランクボディ' . "\n";
}
public function footer(): void
{
echo '共通フッター' . "\n";
}
}
class SilverRankTemplate extends AbstractTemplate
{
public function header(): void
{
echo 'Silverランクヘッダー' . "\n";
}
public function body(): void
{
echo 'Silverランクボディ' . "\n";
}
public function footer(): void
{
echo '共通フッター' . "\n";
}
}
class GoldRankTemplate extends AbstractTemplate
{
public function header(): void
{
echo 'Goldランクヘッダー' . "\n";
}
public function body(): void
{
echo 'Goldランクボディ' . "\n";
}
public function footer(): void
{
echo '共通フッター' . "\n";
}
}
$template = new GoldRankTemplate();
$template->view();
まず、画面を表示する「Template」クラスに表示と関係ない処理を持たなくて良くなります。
つまりクラスの責務として描画に集中できているわけです。
また、抽象クラス側でメソッドの実行順序を決めているので、実行順序の誤りがなくなります。
たとえば、ヘッダーを描画する前にボディーを描画してしまうようなミスを無くせます。
抽象クラスの継承
Template Methodパターンは、インタフェースではなく抽象クラスを継承する形となっています。
これを利用して、例の中の「共通フッター」を抽象クラスに持たせることも可能です。
<?php
abstract class AbstractTemplate
{
public abstract function header(): void;
public abstract function body(): void;
public function footer(): void
{
echo '共通フッター' . "\n";
}
public function view(): void
{
$this->header();
$this->body();
$this->footer();
}
}
class BronzeRankTemplate extends AbstractTemplate
{
public function header(): void
{
echo 'Bronzeランクヘッダー' . "\n";
}
public function body(): void
{
echo 'Bronzeランクボディ' . "\n";
}
}
class SilverRankTemplate extends AbstractTemplate
{
public function header(): void
{
echo 'Silverランクヘッダー' . "\n";
}
public function body(): void
{
echo 'Silverランクボディ' . "\n";
}
}
class GoldRankTemplate extends AbstractTemplate
{
public function header(): void
{
echo 'Goldランクヘッダー' . "\n";
}
public function body(): void
{
echo 'Goldランクボディ' . "\n";
}
public function footer(): void
{
echo 'Goldランクフッター' . "\n";
}
}
$template = new BronzeRankTemplate();
$template->view();
footerメソッドの実装をAbstractTemplateに持たせました。
BronzeランクとSilverランクはfooterメソッドを実装しないことで、共通フッターを表示します。
こうすることで「同じような実装」が重複しなくなります。
一方、別のfooterを表示したい場合は、GoldRankTemplateのように、オーバーライドすることで、実現できます。
このように、実装を持つことができるのが抽象クラスがインタフェースと違う点です。
今回は、抽象クラスの説明も行ってしまいました笑
処理の流れを担保しつつ、柔軟な実装を持たせたい場合はTemplate Methodパターンを検討してみると良いでしょう。