ブリッジってなに?
橋ですね。継承の多用でカオスになってアレしてコピペしないためのパターンです。
でも、これ橋なのかなぁ?
橋ではなくてコマンドなんじゃないのかなぁ?
考え方としてはテンプレートメソッドの抽象メソッド部分を差し替え可能な振舞オブジェクトに移譲するということです。つまり、テンプレートメソッドとコマンドパターンとステートの隠し子みたいなものです。
ブリッジの構造
- ブリッジクラス - 具体的な事柄を表現するクラス
- 実装クラス - 差し替え可能なロジックを表現するクラス
例えば、渡された引数を元にSQL文を自動生成するようなクラスが有ったとします。SQL文にはDBMS毎に微妙な構文の違いがあったりします。この時、ブリッジパターンを適用すると以下のようになります。
- SQL文を自動生成するクラス - ブリッジクラス
- DBMS毎のSQL文を生成するクラス - 実装クラス
このSQL文を自動生成するクラスを継承してDAOとORマッパーを作ったとします。運用の中で対応するDBMSが増えても、実装クラスさえ差し替えてしまえばDAOもORマッパーも何も気にすることなく利用することが出来ます。もし、ここでブリッジパターンを適用せずにDBMSごとのクラスを作っていた場合、DAO、ORマッパー共によく似たクラスをコピペで作る必要が生まれてしまいますね。
ここまで具体的な例を使って説明しておきながら、サンプルコードはサンプルコードらしいサンプルコードにしてしまいます。例が大仰過ぎたので仕方ないのです。
<?php
class BridgeSample {
private $_Impl = null;
public function __construct ( $impl ) {
if ( !( $impl instanceof IImplement ) )
throw new Exception ( '無理っす' );
$this->_Impl = $impl;
}
public function doMethod () {
return $this->_Impl->doMethod ();
}
}
class Subclass1 extends BridgeSample {}
class Subclass1_2 extends Subclass1 {}
class Subclass2 extends BridgeSample {}
class Subclass2_2 extends Subclass2 {}
interface IImplement {
public function doMethod ();
}
class ImpleSample1 {
public function doMethod () {
return "実装1";
}
}
class ImpleSample2 {
public function doMethod () {
return "実装2";
}
}
$parent = new BridgeSample ( new ImpleSample2 () );
say ( $parent->doMethod() == '実装2' ? "OK" : "NG" );
$subclass1 = new Subclass1 ( new ImpleSample1 () );
say ( $subclass1->doMethod() == '実装1' ? "OK" : "NG" );
この例なら、大まかな処理の流れについては、BridgeSampleさえ修正・追記すればSubclass達に反映されるし細かい差分については実装クラスを修正したり、増やしたりすれば何とかなっていきます。
実際どうなの?
メリットが大きくメリットの想像がし易いパターンであるにも関わらず、適用すべきポイントは「あとの祭」になってから気付くことの方が多いです(僕だけかも知れないけど) フレームワークの設計とかなら、割と最初期に適用ポイントを見付けられるかも知れませんが、アプリ内では、「ええーッやっぱりそうだったのー」みたいにならないように、クラス設計時にクラス図を意地になって眺めて適用ポイントを見付けましょう。
注意
また、Bridgeに相当するクラスのメソッド内で複雑なことをやらせると、融通効かなくなってしまいサブクラスでオーバーライドしてその場しのぎしたりすることになったりして、そのサブクラスを継承したクラスが動かなくなったりして収拾付かなくなります。ご利用は計画的に。
併用すべきパターン
ずばりアブストラクトファクトリです。インタンスの生成が複雑になり易いのもそうですが、実装オブジェクトのセットを隠蔽してまとめておくと実装オブジェクトを追加した等の時に修正が容易になります。