読者です 読者をやめる 読者になる 読者になる

Status Code 303 - See Other

Java, C, C++, C#, Objective-C, Swift, bash, perl, ruby, PHP, Python, Scala, Groovy, Go, DevOps, Raspberry Pi など。情報の誤りや指摘・意見などは自由にどうぞ。

Facade - GoF デザインパターン

概要

Facade(ファサードと読むらしい)パターンとは、GoF (Gang of Four)デザインパターンの一つであり、
このパターンが意図することは、サブシステムの簡素なインタフェース提供である。

それ故、Facadeパターン はそれ単体では、新しい機能を提供しない。
複雑なシステムは、簡単なことでもどう実現すればいいか分かりにくくなる。だから簡素化したインタフェースを提供する。
ただし、簡素化することによって機能を追加しやすくはできる。

詳細説明

ユーザは How ではなく What が大事

JPEGなどに用いられる不可逆な画像圧縮は、離散コサイン変換で色情報を縦横の
 周波数成分に変換し、周波数成分の小さい値を省くことによって実現されている」
上記一文は画像圧縮の実現方法だ。大体の人はこれら画像圧縮の詳細な実現方法は
意識していないにも関わらず、これらを日々利用している。

この他にも、HTTP や SSL/TLS などをユーザは日頃実現方法を意識することは
ないにも関わらず、ユーザたちはそれらを用いて、自身のしたいことを平然と行っている。
(ネットでの買い物や、電車の乗換案内、ツイートしたり)

これは使用する機器において、特定機能のツールとして提供されているからだ。
これにより、ユーザは「何ができるか(What)」だけ興味を持ち、
その実現方法(How)は意識しないで済む。

また、使用するシステム自体はとても複雑になったとしても、それらのサブセットを
機能として提供しているからこそ、ユーザはあたかも自分の手足のように動かせる。
このため、ユーザはそれらを使った「自身の仕事をどうするか」だけに注力できる。

複雑なシステムから、意味のある処理の塊を機能として提供することで、ユーザはその詳細を
意識しないで済む。これがまさに Facade パターンの意図することになる。

危険なシステム

あるプログラマが、現在稼働中システムの修正を上司から命じられた。
ある機能にバグがあり、原因処理の切り分けまではほぼ見当がついている。

そのコードを眺めてみると、自分の全く知らないライブラリが数多のクラスで使用されており、
処理の粒度が細かいため、全体の流れが何をしているか分からない
慎重になったプログラマは、インターネットで調査する。

文献はあるにはあったが、全てが外国語。その中にライブラリのドキュメントを見つけた。
それらはやたら大きなドキュメントだったが、幸い、問題の箇所は検討がついている。
プログラマは四苦八苦しながらも日本語訳をして解決を試みる。

─そして、努力甲斐があり、最後に素晴らしい回答にたどり着いた。

そのライブラリのライフサイクルは既に終了しており、
「現在稼働中の環境での動作保証はされない」だった。

このため、ライブラリを取っ替えようかと考えたが、多数のクラスに
処理が記述されているため変更による影響の度合いは計り知れない。
また、その処理を取り替えることができたとして、正しい動作を確認するには、
全てのクラスをテストする必要があり、それはどう考えても間に合わない。

結局、プログラマは、現状で問題のある部分だけを部分的に修復をし、
現在のシステムはもはや保守できるものではないことを上司に報告した。

この話題から得られる教訓

さて、この話題には保守運用の視点から見ると、問題点が数多くある。

ライブラリの機能を利用する人への配慮がない

もし、ライブラリの機能をあるコードが直接1ステップでも利用したのであれば、
このコードに関与した全員がライブラリに対する知識を持たねばならない。
まず、これが非効率だと思わないだろうか?

また、ライブラリを多数用いた複雑なシステムは見通しが良くないため、様々なリスクをはらんでしまう。

  • 解析・影響判定に時間を費やす
  • 修正時におもわぬ影響を発見、大きな手戻り

もし、Facade パターンを利用すれば、ライブラリの機能は間接的に利用されるため、
ライブラリによる影響をあるコード(Facade クラス)だけに閉じ込めることができる。

また、Facade パターンを用いることで、追加の利点が生じる。
Facade が提供する機能を利用することで、利用側はこの機能は何を(What)するかだけを意識することになる。
つまり、詳細な実装内容(How)が利用者側に現れないため、コードが読みやすくなる

ライブラリ変更に対する配慮がない

ライブラリを利用する側は、今後ライブラリに追加して欲しい機能が出てくるかもしれない。
しかし、外部が開発・提供するライブラリは、外部の都合に影響されやすく、その機能を今後追加しないかもしれない。
もっと悪いときには、開発もしくは保守自体がストップすることもある。

古くなったライブラリはセキュリティ的に危険なこともある。また、後々それらが判明することもある。
このような場合を考慮して、ライブラリを取り替える準備をする必要があるはずだ。

Facade パターンを利用すれば、ライブラリを適用している箇所をブラックボックス化できるため、
ライブラリの取っ替えが容易にでき、その変更による影響も特定範囲内になる

Facade パターン適用例

ライブラリの簡素化

テンプレートによる処理とデザインの切り分け - Status Code 303 - See Other
Velocity ライブラリから一部の機能をラッピングして作成しているため、
OutputBuilderクラスのユーザは、Velocityライブラリについて知らなくても良い。

SSHJ による暗号化通信 - Status Code 303 - See Other
SSHJ のFacade クラス。これらを用いることで、ユーザは、SSH を利用する際、
接続→認証→コマンド実行(ファイルダウンロード/アップロード) を行う手順だけを意識すれば良い。
また、もし SSHJ を今後取り替えたい場合は、SSHClientFacade クラス内を実装し直すだけで良い。

(他の例は、あとで適宜更新)

Facade パターン導入効果

既存コードの積極的再利用

Facadeクラスがライブラリのまとめ役の役割なので、裏側では多数のライブラリが
ひしめきあっていたとしても クライアントがそれを気にする必要がない。
つまり、裏側では、より優れた機能があったならライブラリを取り替えても構わない。

可読性向上

Facade クラスは既存機能を単純化しただけなので、そのメソッド名は「何をする」を示すはず。
もし、プログラムが抽象化された処理で記述できるようになれば、プログラムは格段に読みやすくなる。

修正コストの削減

全体的に保守にかかる費用が削減される。各プロセス別に記述すると。

コード解析
何をしているか分かりやすくなるため早くなる
修正に伴う影響判定
処理がまとまっているため影響は小さくなる
コード修正
メソッド内にしか影響がないため容易になる
テスト
特定箇所に絞ってテストできるため容易になる

依存関係の簡素化

ある処理の塊を必要なクラスに直接記述することは、その処理の影響をそのクラスは受け入れることを意味する。
つまり、その処理自体にバグがあったなら、全てのクラスを修正する必要がある。

もし、Facade クラスを作成して作成すれば、その処理の影響範囲はその中だけに納まる。
それは、特定機能がバグの原因が突き止めやすく、テストもそのメソッドだけ行えばいいことになる。

結論

今回の Facadeクラスでは、クラスの関係が以下のようになる。

  • (適用前) クライアント───────<< use >>──────→ サブシステム(ライブラリ)
  • (適用後) クライアント─<< use >>→ Facadeクラス ─<< use >>→サブシステム(ライブラリ)

プログラムの保守性を向上するには、関係クラス間に中間層(上では Facade クラス)を設け利用する考えがある。
これによって、クライアントが利用する機能を中間層でコントロールできるからだ。

注意点として、やりすぎは禁物である。この操作の結果、クラスは元の解決策より増加する。
クラス数が大きくなりだすと、どこに何の機能があるか分かりにくくなり、保守もしにくくなる。
あくまでバランスが大事だということ。

もし、上記に挙げた効果が現在のシステムに非常に良い効果をもたらすと期待できるなら
利用すべきであり、そうでなければやってはいけない
そして、厄介なことに、この明確な基準はおそらくプログラマの力量である。

参考文献

デザインパターンとともに学ぶオブジェクト指向のこころ (Software patterns series)

デザインパターンとともに学ぶオブジェクト指向のこころ (Software patterns series)

更新履歴

2016/01/21 具体例を一部導入して公開。
2016/02/06 例の追加。一部文言修正。