『Java言語で学ぶデザインパターン入門』をPHPで実習する Bridge

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

本記事に掲載したサンプルコードは、https://github.com/ryo-utsunomiya/design_patternでも公開中です。

Bridgeとは

「機能」のクラス階層と「実装」のクラス階層を分離し、それらを独立に変更できるようにすることです。
これによって、「機能」を柔軟に交換できるようになります。

まず、「何かを表示する」という「機能」を持ったDisplayクラスを作成してみます。

<?php

namespace Bridge;

class Display
{
    /**
     * @var DisplayImpl
     */
    private $impl;

    /**
     * Display constructor.
     *
     * @param DisplayImpl $impl
     */
    public function __construct(DisplayImpl $impl)
    {
        $this->impl = $impl;
    }

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

    public function show()
    {
        $this->impl->rawShow();
    }

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

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

このクラスは「何かを表示する」機能の具体的な実装であるDisplayImplに依存しています。DisplayImplは、実装を交換可能にするため抽象クラスにします。

<?php

namespace Bridge;

abstract class DisplayImpl
{
    public abstract function rawOpen();

    public abstract function rawShow();

    public abstract function rawClose();
}

次に、DisplayImplの具象クラスであるStringDisplayImplを実装してみます。

<?php

namespace Bridge;

class StringDisplayImpl extends DisplayImpl
{
    /**
     * @var string
     */
    private $string;

    /**
     * @var int
     */
    private $width;

    /**
     * StringDisplayImpl constructor.
     *
     * @param string $string
     */
    public function __construct($string)
    {
        $this->string = $string;
        $this->width = mb_strlen($string);
    }

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

    public function rawShow()
    {
        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;
    }
}

StringDisplayImplでは、文字列を装飾して表示します。StringDisplayImplを変更しなくても、DisplayImplのサブクラスを作れば、表示方法を変更することができます。

次に、表示する「機能」の方を変更してみます。

<?php

namespace Bridge;

class CountDisplay extends Display
{
    /**
     * CountDisplay constructor.
     *
     * @param DisplayImpl $impl
     */
    public function __construct(DisplayImpl $impl)
    {
        parent::__construct($impl);
    }

    /**
     * @param int $times
     */
    public function multiDisplay($times)
    {
        $this->open();

        for ($i = 0, $times = (int)$times; $i < $times; $i++) {
            $this->show();
        }

        $this->close();
    }
}

CountDisplayは、Displayを拡張し、同じ文言を複数回表示するmultiDisplayメソッドを追加しています。

最後に、これらのクラスの使用例です。

<?php

namespace Bridge;

require_once __DIR__ . '/../autoload.php';

$d1 = new Display(new StringDisplayImpl('Hello, Japan'));
$d2 = new CountDisplay(new StringDisplayImpl('Hello, World'));

$d1->display();
$d2->display();
$d2->multiDisplay(3);

このように、機能と実装を分離することで、機能・実装の両方を柔軟に交換可能にするのがBridgeパターンの特長です。

コメントを残す

コメントを残す