【PHPデザインパターン】05_Façade～シンプルな唯一の窓口

# 引用記事

この記事を書くきっかけになったブログです。

記事内の解説やソースコードは、こちらのブログと著者の公開リポジトリを参考にしています。

> [Do You PHP はてな〜[doyouphp][phpdp]PHPによるデザインパターン入門 - Façade～シンプルな唯一の窓口](http://d.hatena.ne.jp/shimooka/20141215/1418620292)

# 概要

- サブシステム内に存在する複数のインターフェースに1つの統一インターフェースを与える。
- 複雑に連携しあうクラスやオブジェクトを容易に扱うための窓口（API）を提供する。
- クライアントからはサブシステムの入り口しか見えなくなり、同時にサブシステムとの結びつきがゆるくなる。その結果、互いの独立性が高くなる。
- 非常に大きな「カプセル化」を行なっている。
- 「façade」は「ファサード」と読む。（フランス語で「正面窓口」という意味）

## 構成要素

### Façadeクラス

- サブシステムで提供される統一APIを持つクラス。
- サブシステム内のクラス同士の関係を知っている。

### サブシステム内のクラス群

- サブシステムを構成するクラス群。
- Façadeクラスの存在は知らない。

### Clientクラス

- サブシステムを利用するクラス。
- Façadeクラスを通じてサブシステムにアクセスする。
- サブシステム内のクラス同士の関係は見えない。
 
# 実演
著者のソースコードと比べると、かなり簡単な内容になっています。

## 処理の流れ
- 注文データを配列形式で受け取り、メッセージを出力する。
- ``my_client.php``はClientクラスに該当する。
- ``Managerクラス``はFaçadeクラスに該当する。
- ``Orderクラス``と``CalcMessageクラス``をサブシステムとする。
- Orderクラスで注文情報をセットする。
- CalcMessageクラスで合計金額を計算し、注文内容のメッセージを出力する。

## ファイル構造
```
MyFacade
   ├── CalcMessage.php
   ├── Manager.php
   ├── Order.php
   └── my_client.php
```

## ソースコード
### my_client.php
```my_client.php
<?php
namespace DoYouPhp\PhpDesignPattern\Facade\MyFacade;
require dirname(dirname(__DIR__)).'/vendor/autoload.php';

use DoYouPhp\PhpDesignPattern\Facade\MyFacade\Manager;
use DoYouPhp\PhpDesignPattern\Facade\MyFacade\Order;
use DoYouPhp\PhpDesignPattern\Facade\MyFacade\CalcMessage;

// 配列型の注文データ
$order_data = array(1, 'りんご', 100, 3);

// Managerクラスを窓口にして必要な処理を行う
Manager::order($order_data);
```

### Manager.php
```Manager.php
<?php
namespace DoYouPhp\PhpDesignPattern\Facade\MyFacade;

use DoYouPhp\PhpDesignPattern\Facade\MyFacade\Order;
use DoYouPhp\PhpDesignPattern\Facade\MyFacade\CalcMessage;

class Manager
{
    // 注文データを元にサブシステムで必要な処理を行わせる
    public function order($order_data)
    {
        // Orderクラスのインスタンスを生成し、必要なプロパティがセットされる
        // Singletonパターンを利用してオブジェクトの複製を防いでいる
        $order = Order::getInstance($order_data);

        // CalcMessageクラスのインスタンスを生成する
        // 注文内容を計算し、メッセージを返す
        $calc_message = new CalcMessage($order);
        $calc_message->createMessage();

        // cloneを試して複製できないことを確認する
        try {
            $order_clone = clone $order;
        } catch (\RuntimeException $e) {
            echo $e->getMessage().'<br>'."\n";
        }
    }
}
```

### Order.php
```Order.php
<?php
namespace DoYouPhp\PhpDesignPattern\Facade\MyFacade;

// 注文内容をプロパティとして保有する
class Order
{
    private $id;
    private $name;
    private $price;
    private $amount;
    // $instanceはオブジェクト型のプロパティ
    private static $instance;

    // 配列型の注文データからプロパティをセットする
    private function __construct($order_data)
    {
        list($this->id, $this->name, $this->price, $this->amount) = $order_data;
    }

    // 他クラスからオブジェクトを生成する場合は、こちらのメソッドを利用する
    // staticメソッドなので、オブジェクト未作成の状態から呼び出せる
    // オブジェクトが存在しない場合のみ、新規にオブジェクトを生成する
    public function getInstance($order_data)
    {
        if (!isset(self::$instance)){
            self::$instance = new Order($order_data);
        }

        return self::$instance;
    }

    // セットしたプロパティを返す
    public function getId()
    {
      return $this->id;
    }
    
    public function getName()
    {
        return $this->name;
    }

    public function getPrice()
    {
        return $this->price;
    }

    public function getAmount()
    {
        return $this->amount;
    }

    // Singletonパターンを利用してオブジェクトの複製を防ぐ
    final public function __clone()
    {
        throw new \RuntimeException(get_class($this).'クラスのオブジェクトは複製できません');
    }
  }
```

### CalcMessage.php
```CalcMessage.php
<?php
namespace DoYouPhp\PhpDesignPattern\Facade\MyFacade;

// 注文合計額を計算しメッセージを返す
class CalcMessage
{
    // $orderはオブジェクト型のプロパティ
    private $order;
    private $order_sum;

    // Orderオブジェクトを引数にして必要なプロパティをセットする
    public function __construct(Order $order)
    {
        $this->order = $order;

        // 価格*個数の値をプロパティにセットする
        $order_price = $this->order->getPrice();
        $order_amount = $this->order->getAmount();
        $this->order_sum = $order_price * $order_amount;
    }

    // 注文内容と計算結果のメッセージを返す
    public function createMessage()
    {
        echo '注文ID'.$this->order->getId().'で'.$this->order->getName().'を注文しました！'.'<br>'."\n";
        echo '注文合計は'.$this->order_sum.'円です！'.'<br>'."\n";
    }
}
```