ファサードって?
要するに「一箇所に全部書くようなヤツはGoFの手によって地獄の業火に投げ込まれるべき」パターンとか「リファクタリングは良いことだ」パターンと呼べそうなパターンです。教訓的意味合いの強いパターンかも知れません。
ファサードは「窓口」という意味です。例えば、MVCフレームワークで作っている時に、やたらめったらコントローラに書いてしまったりすることは無いでしょうか?本来、WebMVCにおけるコントローラはWebシステムに対する依存と業務ロジックを切り離すことを目的として設けられるものです。つまり、大まかに言うとコントローラの役割は以下になりますね。
- Webからの入力の妥当性検証(単純なvalidate)をする
- Webからの入力を業務ロジックに渡す
- 業務ロジックから受け取ったデータを元に業務ロジックを走らせ「HTMLを返す」、「リダイレクトする」、「404、403」などの判断をする
となるはずなのに、はじめはちょっとモデルを叩くだけだったはずが、あーだこーだと改修しているうちにやたら巨大なコントローラが出来上がったりします。単に実行タイミングが一緒であるというだけで意味合い的にも相互関係性が薄い処理を同じところ、あろうことかコントローラに直接追記していったりするから救いようが無かったりしますね。こういう時にファサードパターンを適用します。
ファサードの構造
ファサードには使う側と使われる側という構造しかありません。
- ワーカー - ファサードから使われる側
- ファサード - ワーカーを使う側
大きなシステムになるとファサードのファサード、ファサードのファサードのファサードのファサードみたいなものも出来たりするかも知れません。
<?php
class Facade {
public function run () {
$obj1 = new Worker1();
$obj2 = new Worker2();
$obj3 = new Worker3();
if ( $obj1->doMethod() ) return $obj2->doMethod();
else return $obj3->doMethod();
}
}
class Worker1 {
public function doMethod () {
return true;
}
}
class Worker2 {
public function doMethod () {
return 'worker2';
}
}
class Worker3 {
public function doMethod () {
return 'worker3';
}
}
$obj = new Facade();
print $obj->run ();
「ほげ?」と思った方へ
コード例だけを見てもイマイチよく分からないですね。普通過ぎるコードです。そこで役割について補足することにします。
端的に言うと「より小さい単位の処理を繋ぎ合わせるのがFacadeです」ということなのです。「OOPなら当たり前じゃボケぇ」というのはごもっとも。では、言い方を変えましょう。「FacadeはWorkerの処理する順番を知っているというだけなのだ」と言うとどうでしょう?そうすると、上の例で言えば、Worker1->doMethod() は Worker2->doMethod() との 関係性を気にせずに実装することが出来ます。Worker1とWorker2の結合度を下げることが出来ますね。Facadeのrunの中ではあまり一時的な変数は置かないようにして順番に従ってメソッドを実行するだけが理想的です。
newしてるけど良いの?
「メソッド内でnewして、そのままメソッド呼んでるけど良いの?」と単体テスト書きたい人は思ったことでしょう。実際問題としてはケースバイケースです。「newして実行する」という「最終的な依存を押し付ける役」としてFacadeを利用するというのは良いことにしてあげたいです。なので、そのFacadeについては極力複雑性を排した形で実装してあげたいですね。このnewする問題を受け入れた上で単体テストをする際のコツは以下です。
- 対象のメソッドで new しない代わりに new するメソッドを自身に実装する
- テスト時にinclude_pathの優先度を入れ変える
前者については Facade->run のテストをする時、Facadeを継承してサブクラスを作って行うということです。継承した先で、newするメソッドをオーバーライドしモックに差し替えてやれば良いです。後者については元コードを弄らない代りにinclude_pathの優先順を変えて、同じ名前のファイルでダミークラスを記述しているファイルを先に読ませるということです。
もういっちょ補足
Facadeは生成に関するパターンの各種とTemplateメソッドと組み合わせて作ることにより結合度を下げたり重複処理を排除したりすることが出来ます。(ここでやっとクラス設計の妙味が出てきます)