PHPでデザインパターン「Bridgeパターン」
GoFデザインパターンをPHPでプログラムを書いてみながら勉強していきます。「増補改訂版 Java言語で学ぶデザインパターン入門」を参考にさせて頂いています。プログラムはGithubにあげていきます。
Bridgeパターンとは
bridgeは日本語で「橋」という意味です。Bridgeパターンとは、「機能のクラス階層」と「実装のクラス階層」を独立したクラス階層に分けて、分けたクラスを結びつけるデザインパターンを指します。
Bridgeパターンの構造
- Abstractionクラス 「機能のクラス階層」の最上位のクラスです。
- RefinedAbstractionクラス Abstractionクラスを継承し、機能を追加したサブクラスです。
- Implementorクラス 「実装のクラス階層」の最上位のクラスです。Abstractionクラスのインタフェースを実装するためのメソッドを規定するクラスです。
- ConcreteImplementorクラス 具体的にImplementorクラスのインタフェースを実装するImplementorクラスのサブクラスです。
左のAbstractionクラス、RefinedAbstractionクラスが「機能のクラス階層」、右のImplementorクラス、ConcreteImplementorクラスが「実装のクラス階層」になります。
機能を追加したい時にAbstractionクラスのサブクラスとして新しいクラスを追加させていきます。実装を追加したい時にImplementorクラスのサブクラスとして新しいクラスを追加させていきます。Implementorクラスは抽象クラスなので、追加したサブクラスでは抽象メソッドを実装することになります。
実装と機能って何が違うの?というところなのですが。
実装はアプリケーションが意識しない処理で、機能の追加は仕様として必要な処理、という認識です。例えば、何かデータを取得する場合、どこからデータを取得するかはアプリケーション側は気にしなくていい部分なので実装側に、データをどのように出力するかは仕様の部分で意識しておくべき処理なので機能側に実装するイメージです。違ってたら誰か教えて。
Bridgeパターンのメリット
- 拡張が楽になる
- クラス階層が深くなることを防げる
- クラスの数が減る
Bridgeパターンの実装例
テキストファイルを読み込んで、出力させる機能を実装します。出力データから半角スペースを取り除くという機能を追加したSubReadingクラスに追加しています。
機能のクラス階層の最上位であるReadingクラスです。コンストラクタでdataオブジェクトを受け取り、各メソッドはdataオブジェクトのメソッドを呼び出しています。
<?php namespace DesignPatterns\Src\Structural\Bridge; class Reading { private $data; public function __construct($data) { $this->data = $data; } public function open() { return $this->data->open(); } public function read() { return $this->data->read(); } public function close() { return $this->data->close(); } }
Readingクラスに機能追加したSubReadingクラスです。readWithRemovingSpace()では、Readingクラスのread()メソッドを呼び出しています。
<?php namespace DesignPatterns\Src\Structural\Bridge; class SubReading extends Reading { public function readWithRemovingSpace() { $text = $this->read(); array_walk_recursive($text, 'self::removeSpaceString'); return $text; } private static function removeSpaceString(&$string) { $string = str_replace(array(" ", " "), "", $string); } }
実装のクラス階層の最上位であるDataAbstractionクラスです。抽象クラスです。抽象メソッドを規定しています。
<?php namespace DesignPatterns\Src\Structural\Bridge; abstract class DataAbstract { abstract protected function open(); abstract protected function read(); abstract protected function close(); }
DataAbstractionクラスの抽象メソッドを実装しているFileクラスです。
<?php namespace DesignPatterns\Src\Structural\Bridge; class File extends DataAbstract { private $fileName; private $handle; public function __construct($fileName) { $this->fileName = $fileName; } public function open() { $this->handle = fopen($this->fileName, 'r'); if(!$this->handle){ throw new Exception('file open failed'); } return; } public function read() { $data = array(); while(!feof($this->handle)){ $tmp = rtrim(fgets($this->handle)); if($tmp !== '') $data[] = $tmp; } return $data; } public function close() { fclose($this->handle); return; } }
関連パターン
- TemplateMethodパターン
- AbstractFactoryパターン
- Adapterパターン
まとめ
機能側が実装側を意識せず使えるのがいい。ただ、機能と実装を整理した上でパターンを適用しないと、処理があっちこっちに散らばってしまって逆にメンテナンスの悪いコードになりそうです。