1. Home
  2. PHP
  3. DesignPatterns
  4. Facadeパターン - PHPデザインパターン

Facadeパターン - PHPデザインパターン

  • 公開日
  • カテゴリ:DesignPatterns
  • タグ:PHP,Facade,DesignPatterns,Structure
Facadeパターン - PHPデザインパターン

Facade パターン(ファサード・パターン)は、構造に関するデザインパターン手法の一つで、サブシステムをまとめその窓口となるクラスを作成する事で、複雑な一連の処理を意識する事なくシンプルにその機能を利用できる処理モデルです。

Facade パターンの基本的なクラス図は以下の通りです。

実装例

今回は、BTO でパソコンのカスタマイズを行う場合を例として Facade パターンを実装します。

BTO(Build to PostsListOrder = ビルド・トゥ・オーダー)と言えば、パーツやスペックを好きなように選んで注文できる便利な購買システムですが、各パーツの情報や組み合わせ、金額計算、注文処理など、裏側では色々な処理が動いており、PHP で実装しても複雑化しやすいパターンの一つとも言えます。

ただでさえ接客に忙しい店長は、このシステムの処理にかかり切りになるわけにはいかないと、これらの処理を Facade パターンで実装する事にしました。

まずはサブシステム群から。パーツ情報を扱う Item クラスです。

App/Bto/Item.php
<?php

namespace App\Bto;

class Item
{
    private $id;
    private $name;
    private $price;

    public function __construct($id, $name, $price)
    {
        $this->id = $id;
        $this->name = $name;
        $this->price = $price;
    }

    public function getName()
    {
        return $this->name;
    }
    public function getPrice()
    {
        return $this->price;
    }
    public function display()
    {
        echo sprintf('%s \\%s<br>', $this->getName(), number_format($this->getPrice()));
    }
}

パソコンの BTO なので、基本モデル・ CPU ・メモリ・ HDD あたりのチョイスを設けようと思いますが、それらの情報を共通のオブジェクトで持つためのクラスです。

次は、選択するパーツ情報のクラスです。基本モデル・ CPU ・メモリ・ HDD のクラスを作成します。

App/Bto/MachineModel.php
<?php

namespace App\Bto;

use App\Bto\Item;

class MachineModel
{
    private $items;

    public function __construct()
    {
        $this->items = [
            1 => new Item(1, 'ミニタワー', 10000),
            2 => new Item(2, 'ミドルタワー', 20000),
            3 => new Item(3, 'フルタワー', 30000)
        ];
    }

    public function getItem($id)
    {
        return $this->items[$id];
    }
}
App/Bto/PartsCpu.php
<?php

namespace App\Bto;

class PartsCpu
{
    private $items;

    public function __construct()
    {
        $this->items = [
            1 => new Item(1, 'Core i3 プロセッサー', 10000),
            2 => new Item(2, 'Core i5 プロセッサー', 20000),
            3 => new Item(3, 'Core i7 プロセッサー', 30000)
        ];
    }
    public function getItem($id)
    {
        return $this->items[$id];
    }
}
App/Bto/PartsMemory.php
<?php

namespace App\Bto;

class PartsMemory
{
    private $items;

    public function __construct()
    {
        $this->items = [
            1 => new Item(1, '4MB', 10000),
            2 => new Item(2, '8MB', 20000),
            3 => new Item(3, '16MB', 30000)
        ];
    }
    public function getItem($id)
    {
        return $this->items[$id];
    }
}
App/Bto/PartsHdd.php
<?php

namespace App\Bto;

class PartsHdd
{
    private $items;

    public function __construct()
    {
        $this->items = [
            1 => new Item(1, 'SSD 128GB', 10000),
            2 => new Item(2, 'SSD 256GB', 20000),
            3 => new Item(3, 'SSD 500GB', 30000)
        ];
    }
    public function getItem($id)
    {
        return $this->items[$id];
    }
}

中身はシンプルな感じですが、これらはデータベースから情報を取得するイメージのクラスにしています。(実際はデモなのでベタ書き)

次は、金額計算を行うクラスです。選択したパーツの合計金額を処理します。

App/Bto/TotalFee.php
<?php

namespace App\Bto;

class TotalFee
{
    private $total = 0;

    public function add($price)
    {
        $this->total = $this->total + $price;
    }

    public function getTotal()
    {
        return sprintf('\\%s-', number_format($this->total));
    }
}

サブシステムの最後は、選択したパーツモデルを管理するクラスです。

App/Bto/Choose.php
<?php

namespace App\Bto;

class Choose
{
    private $chooses = [];

    public function getChooses()
    {
        return $this->chooses;
    }
    public function add($item)
    {
        $this->chooses[] =  $item;
    }
}

さて。これで選択情報・パーツ情報・金額処理を扱うクラスが揃ったのでクライアントから利用し一連の BTO システムを構築してみます。

と言いたいところですが、これらをクライアントで利用しようとすると結構な数の処理の取り回しが必要になる事が想像できます。これをクライアント側に投げるのは少し酷なようです。

ここで Facade パターンの出番です。クライアントコードに着手する前に、Facade クラスを作成します。

App/Bto/BtoFacade.php
<?php

namespace App\Facade;

use App\Bto\MachineModel;
use App\Bto\PartsCpu;
use App\Bto\PartsHdd;
use App\Bto\PartsMemory;
use App\Bto\TotalFee;
use App\Bto\Choose;

class BtoFacade
{
    private $machine_model;
    private $parts_cpu;
    private $parts_memory;
    private $parts_hdd;
    private $total_fee;
    private $choose;

    public function __construct()
    {
        $this->machine_model = new MachineModel();
        $this->parts_cpu = new PartsCpu();
        $this->parts_memory = new PartsMemory();
        $this->parts_hdd = new PartsHdd();
        $this->total_fee = new TotalFee();
        $this->choose = new Choose();
    }

    public function chooseModel($id)
    {
        $item = $this->machine_model->getItem($id);
        $this->choose->add($item);
        $this->total_fee->add($item->getPrice());
    }
    public function chooseCpu($id)
    {
        $item = $this->parts_cpu->getItem($id);
        $this->choose->add($item);
        $this->total_fee->add($item->getPrice());
    }
    public function chooseMemory($id)
    {
        $item = $this->parts_memory->getItem($id);
        $this->choose->add($item);
        $this->total_fee->add($item->getPrice());
    }
    public function chooseHdd($id)
    {
        $item = $this->parts_hdd->getItem($id);
        $this->choose->add($item);
        $this->total_fee->add($item->getPrice());
    }

    public function Total()
    {
        foreach ($this->choose->getChooses() as $item) {
            $item->display();
        }
        echo sprintf('<br>合計 %s', $this->total_fee->getTotal());
    }
}

Facade クラスにて、関連クラスを用いた情報取得・保持・参照・合計処理までを実装しています。

それぞれのメソッド上、つまりは1つの処理に対して複数のクラスが絡む事は良くありますが、Facade パターンではこの辺りも吸収してこのようにすっきりと処理をまとめる事ができます。そしてクライアント側では、関連するクラスを意識せずに Facade クラスのみを用いて一連の処理を実現できるようになります。

では、クライアントからこれらを使用します。

index.php
<?php

use App\Facade\BtoFacade;

$bto = new BtoFacade();
$bto->chooseModel(rand(1, 3));
$bto->chooseCpu(rand(1, 3));
$bto->chooseMemory(rand(1, 3));
$bto->chooseHdd(rand(1, 3));

echo $bto->Total();

各 choose() メソッドでは、選択したパーツの ID を渡してパーツ情報をセットしています。(今回は、ランダムに番号を発行しています)

そして、Total() メソッドで結果を出力している。これだけです。

結果は以下のようになります。

Facade パターンを用いてクライアントから一連の処理をシンプルに使う事が出来ました。

まとめ

「Facade = 正面・窓口」を意味する通り、Facade クラス自体は特別な処理を持たず、サブシステムを束ね処理を中継する事に注力します。そしてこれによってクライアント側では、複雑なクラス同士の絡みや処理の手順などを気にする事なく、シンプルに処理を利用できるようになります。

今回の実装例ではサブシステムをまとめる為に単純なクラスを複数作成しましたが、実際の現場ではその1つ1つの機能や役割が大きな責務を負っているクラスも多いので、クライアント側から無作為に利用するよりも、一連の処理の流れのまとまりとして Facade パターンを用いると、システム構築もシンプルになります。是非試してみてください。

Author

rito

  • Backend Engineer
  • Tokyo, Japan
  • PHP 5 技術者認定上級試験 認定者
  • 統計検定 3 級