PHP7でデザインパターン入門9/23 Bridgeパターン

Bridge パターンとは

「橋渡し」のクラスを用意することによって、クラスを複数の方向に拡張させることを目的とする。

Bridge パターン - Wikipedia

この「橋渡し」をする場所は、「機能のクラス階層」と「実装のクラス階層」である。

機能のクラス階層

新しい「機能」を追加したい場合、以下のような階層となる。

SomethingClass
  └ ChildSomethingClass
    └ GrandChildSomethingClass

実装のクラス階層

新しい「実装」を追加したい場合、以下のような階層となる。

SomethingAbstractClass
  └ ConcreteClass
  └ ConcreteClass2

example

以下のサンプルコードは「何かを表示」するためのもの。

DisplyaImpl 抽象クラス

<?php
abstract class DisplayImpl
{
    abstract public function rawOpen();
    abstract public function rawPrint();
    abstract public function rawClose();
}

Display クラス

<?php
class Display
{
    private $impl;

    public function __construct(DisplayImpl $impl)
    {
        $this->impl = $impl;
    }

    public function open()
    {
        $this->impl->rawOpen();
    }

    public function print()
    {
        $this->impl->rawPrint();
    }

    public function close()
    {
        $this->impl->rawClose();
    }

    final public function display()
    {
        $this->open();
        $this->print();
        $this->close();
    }
}

CountDisplayクラス

<?php
class CountDisplay extends Display
{
    public function __construct(DisplayImpl $impl)
    {
        parent::__construct($impl);
    }

    public function multiDisplay(int $times)
    {
        $this->open();
        for ($i = 0; $i < $times; $i++) {
            $this->print();
        }
        $this->close();
    }
}

StringDisplayImplクラス

<?php
class StringDisplayImpl extends DisplayImpl
{
    private $string;
    private $width;

    public function stringDisplayImpl(string $string)
    {
        $this->string = $string;
        $this->width = strlen($string);
    }

    public function rawOpen()
    {
        $this->printLine();
    }

    public function rawPrint()
    {
        echo "|" . $this->string . "|" . PHP_EOL;
    }

    public function rawClose()
    {
        $this->printLine();
    }

    private function printLine()
    {
        echo "+";
        for ($i = 0; $i < $this->width; $i++) {
            echo "-";
        }
        echo "+" . PHP_EOL;
    }
}

Main

<?php
$d1 = new Display(new StringDisplayImpl("Hello, Japan."));
$d2 = new CountDisplay(new StringDisplayImpl("Hello, World."));
$d3 = new CountDisplay(new StringDisplayImpl("Hello, Universe."));
$d1->display();
$d2->display();
$d3->display();
$d3->multiDisplay(5);

実行結果

$ php Main.php
+-------------+
|Hello, Japan.|
+-------------+
+-------------+
|Hello, World.|
+-------------+
+----------------+
|Hello, Universe.|
+----------------+
+----------------+
|Hello, Universe.|
|Hello, Universe.|
|Hello, Universe.|
|Hello, Universe.|
|Hello, Universe.|
+----------------+

ソースコード

github.com

役割

  • Abstraction: 機能クラス階層の最上位クラス。Displayクラス
  • FefinedAbstraction: 改善した抽象化の役。CountDisplayクラス
  • Implementor: 実装者。実装のクラス階層の最上位クラス。DisplayImplクラス。
  • ConcreteImplementor: 具体的な実装者。StringDisplayImplクラス。

まとめ

  • Bridgeパターンの特徴は「機能のクラス階層」と「実装のクラス階層」を分けている点。この2つのクラス階層を分けておけば、それぞれのクラス階層を独立に拡張できる。機能を追加したければ機能のクラス階層にクラスを追加する。このとき、実装のクラス階層は修正する必要がない。さらに、追加した機能は「すべての実装」で利用できる。

  • 例として、OS依存の部分があるプログラムにBridgeパターンを適用することを考える。そのOS依存の部分をBridgeパターンの「実装のクラス階層」で表現し、各OS共通部分のインターフェースを決めてImplementor役とし、ConcreteImplementor役として各OS(win, mac, linuxなど)それぞれのクラスを作る。こうすることによって、「機能のクラス階層」側でいくら機能を追加しても、各OS同時に対応していることになる。

  • 2種類のクラス階層に分離することで、クラス拡張を見通しよく行うことができる。

関連パターン

増補改訂版Java言語で学ぶデザインパターン入門

増補改訂版Java言語で学ぶデザインパターン入門