LoginSignup
3
6

More than 5 years have passed since last update.

PHPで学ぶデザインパターン(Bridgeパターン)

Last updated at Posted at 2017-05-03

Bridgeパターンとは

機能のクラス階層と実装のクラス階層を橋渡しするデザインパターンのことです。
それにより機能のクラスと実装のクラスを独立して拡張することができます。

機能のクラス階層と実装のクラス階層とは

機能のクラス階層

スーパークラスで基本的な機能を持っていて、サブクラスで新しい機能を追加するようなイメージです。

実装のクラス階層

スーパークラスで抽象メソッドによってインターフェースを規定しており、サブクラスでその抽象メソッドを実際に実装しているようなイメージです。

今回具体的なコードの例を下に書きますが、その概要を先にまとめると
機能のクラス階層としては
「表示する」という基本的な機能を持ったDisplayクラスと
Displayクラスを継承してさらに「n回表示する」という機能を追加したCountDisplayクラスがあります
表示するという機能を持っていますが、何を表示させるのか、どのように表示させるのかという実装はされていません。

実装のクラスとしては
「表示をさせるためのメソッド」、その「前後の処理のメソッド」を規定したDisplayImplインターフェースと
DisplayImplインターフェースで規定したメソッドを実装するStringDisplayImplがあります。
実際に表示をさせるためのメソッドなので、何を表示させるのか、どのように表示させるのかという実装がされています。

階層を分けるメリット

機能と実装を分けることで、それぞれ独立して拡張することができます。
「n回表示する」以外にも追加で機能を増やしたい時には機能のクラス階層を拡張すればよく、実装のクラス階層を意識する必要はありません。
また、表示させたいものを追加する場合にも実装のクラスのみ追加すればよく、機能のクラスを意識する必要はありません。

簡単なコードを書いてみる

機能のクラス階層

Displayクラスでは「表示する」という基本的な機能だけを持っています。
何を表示するのかはコンストラクターで渡すようになっており、実装もそちらで行います。

class Display
{
    private $impl;

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

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

    public function output()
    {
        $this->impl->rawOutput();
    }

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

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


CountDisplayクラスではDisplayクラスを継承し、「指定回数だけ表示する」という機能を追加しています。
何を表示するのかはコンストラクターで渡すようになっており、実装もそちらで行います。

class CountDisplay extends Display
{
    public function __construct(DisplayImpl $displayImpl)
    {
        parent::__construct($displayImpl);
    }

    public function multiDisplay($times)
    {
        $this->open();
        for ($i = 0; $i < $times; $i++) { // times回繰り返して表示するという機能を追加している
            $this->output();
        }
        $this->close();
    }
}

実装のクラス階層

DisplayImplインターフェースで規定さえているメソッドは、Displayクラスのopen,output,closeに対応しています。
そのためDisplayImplインターフェースを継承していれば、Displayクラスのコンストラクタへ引数として渡すことができます。

interface DisplayImpl
{
    public function rawOpen(); // 前処理

    public function rawOutput(); // 表示

    public function rawClose(); // 後処理
}

StringDisplayImplクラスで実装していきます。

class StringDisplayImpl implements DisplayImpl
{
    private $string;
    private $width;

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

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

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

呼び出し

$d1 = new Display(new StringDisplayImpl("hello"));
$d1->display();

+-----+
|hello|
+-----+

$d2 = new CountDisplay(new StringDisplayImpl("hello"));
$d2->display();

+-----+
|hello|
+-----+

$d3 = new CountDisplay(new StringDisplayImpl("hello"));
$d3->multiDisplay(3);

+-----+
|hello|
|hello|
|hello|
+-----+

これで一通りできました。
もし表示させるものを変えたい、実装を変えたいと思ったらDisplayImplを継承したクラスを作成すればればよく、DisplayImplなどの機能の階層に影響を及ぼすことはありません。

参考

Java言語で学ぶデザインパターン入門

3
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
6