Abstract Factoryパターン

会員ランクごとに画面イメージを切り替えよう パート2

会員のランクによって画面(Web)のイメージを変えたいと思いました。 さっそく書いてみましょう。


interface Template
{
  public function show(): void;
}

class BronzRankHeaderTemplate implements Template
{
  public function show(): void
  {
    echo 'Bronzeランクヘッダー' . "\n";
  }
}
class BronzRankBodyTemplate implements Template
{
  public function show(): void
  {
    echo 'Bronzeランクボディ' . "\n";
  }
}

class SilverRankHeaderTemplate implements Template
{
  public function show(): void
  {
    echo 'Silverランクヘッダー' . "\n";
  }
}
class SilverRankBodyTemplate implements Template
{
  public function show(): void
  {
    echo 'Silverランクボディ' . "\n";
  }
}

class CommonFooterTemplate implements Template
{
  public function show(): void
  {
    echo '共通フッター' . "\n";
  }
}

class GoldRankHeaderTemplate implements Template
{
  public function show(): void
  {
    echo 'Goldランクヘッダー' . "\n";
  }
}
class GoldRankBodyTemplate implements Template
{
  public function show(): void
  {
    echo 'Goldランクボディ' . "\n";
  }
}
class GoldRankFooterTemplate implements Template
{
  public function show(): void
  {
    echo 'Goldランクフッター' . "\n";
  }
}

$header = new GoldRankHeaderTemplate();
$header->show();
$body = new GoldRankBodyTemplate();
$body->show();
$footer = new GoldRankFooterTemplate();
$footer->show();

画面表示用の部品が用意されていて、ランクごとに組み合わせて使えるようになっています。 しかし、次のような不具合が生まれる可能性もあります。

$header = new BronzRankHeaderTemplate();
$header->show();
$body = new SilverRankBodyTemplate();
$body->show();
$footer = new GoldRankFooterTemplate();
$footer->show();

ブロンズ会員用のヘッダーに、シルバー会員用のボディー。 仕様上はありえない組み合わせですね。

Abstract Factoryパターンで防いでみましょう

Abstract Factoryパターンとは

決まった組み合わせのインスタンスを生成するパターンです.

abstract class AbstractFactory
{
  public abstract function getHeader(): Template;
  public abstract function getBody(): Template;
  public abstract function getFooter(): Template;
}

interface Template
{
  public function show(): void;
}

class BronzRankHeaderTemplate implements Template
{
  public function show(): void
  {
    echo 'Bronzeランクヘッダー' . "\n";
  }
}
class BronzRankBodyTemplate implements Template
{
  public function show(): void
  {
    echo 'Bronzeランクボディ' . "\n";
  }
}

class SilverRankHeaderTemplate implements Template
{
  public function show(): void
  {
    echo 'Silverランクヘッダー' . "\n";
  }
}
class SilverRankBodyTemplate implements Template
{
  public function show(): void
  {
    echo 'Silverランクボディ' . "\n";
  }
}

class CommonFooterTemplate implements Template
{
  public function show(): void
  {
    echo '共通フッター' . "\n";
  }
}

class GoldRankHeaderTemplate implements Template
{
  public function show(): void
  {
    echo 'Goldランクヘッダー' . "\n";
  }
}
class GoldRankBodyTemplate implements Template
{
  public function show(): void
  {
    echo 'Goldランクボディ' . "\n";
  }
}
class GoldRankFooterTemplate implements Template
{
  public function show(): void
  {
    echo 'Goldランクフッター' . "\n";
  }
}

class BronzeRankTemplateFactory extends AbstractFactory
{
  public function getHeader(): Template
  {
    return new BronzRankHeaderTemplate();
  }

  public function getBody(): Template
  {
    return new BronzRankBodyTemplate();
  }
  public function getFooter(): Template
  {
    return new CommonFooterTemplate();
  }
}

class SilverRankTemplateFactory extends AbstractFactory
{
  public function getHeader(): Template
  {
    return new SilverRankHeaderTemplate();
  }

  public function getBody(): Template
  {
    return new SilverRankBodyTemplate();
  }
  public function getFooter(): Template
  {
    return new CommonFooterTemplate();
  }
}

class GoldRankTemplateFactory extends AbstractFactory
{
  public function getHeader(): Template
  {
    return new GoldRankHeaderTemplate();
  }

  public function getBody(): Template
  {
    return new GoldRankBodyTemplate();
  }
  public function getFooter(): Template
  {
    return new GoldRankFooterTemplate();
  }
}

$template = new GoldRankTemplateFactory();
$header = $template->getHeader();
$header->show();
$body = $template->getBody();
$body->show();
$footer = $template->getFooter();
$footer->show();

ランクに従ったTemplate Factoryを選ぶことで、想定外の組み合わせを防ぐことができます。

しかし、インスタンスの呼び出し順を決めることはできないので、呼び出し順も決めたい場合は、 Template Methodパターンと組み合わせることも検討すると良いでしょう。