―――――――――――――――――――――――――――――― ■デザインパターン ―――――――――――――――――――――――――――――― ■前提 ・安定した既存のコードに手を加えずに流用できるようにする手法。 (毎回過去のコードに手を加えて利用すると、いつまでたってもコードが安定しない。) ・プログラムの再利用性を高めるもの。クラスの責任を明確にする物。 ・他プログラマが書いたコードを読む手がかりになったり、他プログラマと意思の疎通がやりやすくなったりする。 ・万能なものではないが、一般常識として知っておきたい。以下も一読しておくと良い。 http://www.happiese.com/system/dpattern.html http://qiita.com/irxground/items/d1f9cc447bafa8db2388 http://b.hatena.ne.jp/entry/qiita.com/irxground/items/d1f9cc447bafa8db2388 http://qiita.com/magicant/items/64685c41fdb85d03c33b ・「DIコンテナ」など、ここに含まれないパターンも登場している。 ・「Template Method」「Singleton」「Adapter」「Factory Method」「Facade」「Iterator」 あたりまでをある程度理解していれば、いったん他は「こんなものがある」くらいで十分かもしれない。 ■基本知識 ・オブジェクト指向 カプセル化 ... 情報の隠蔽。用意された操作方法を知っていれば、操作手順やそれに伴うデータについて知らなくていい。 継承 ... 他のクラスの特徴を引き継ぎ、新しい特徴を加えたり変更したりできる。 ポリモーフィズム ... あるオブジェクトへの操作が、呼び出し側ではなく受け手のオブジェクトによって定まる。 http://www.nulab.co.jp/designPatterns/designPatterns1/designPatterns1-4.html ・アクセス制御 public ... すべてのクラスからアクセスできる。 private ... 同じクラス内からのみアクセスできる。 protected ... 同じクラス内とサブクラスからアクセスできる。 ・抽象クラスと抽象メソッド メソッドの先頭に abstract を付けたものが抽象メソッド。これは処理の内容が無いメソッドで、サブクラスに実装を任せる。 抽象メソッドが一つでも含まれるクラスは抽象クラスと呼ばれ、クラスの先頭に abstract を付ける。 ・インターフェース 抽象メソッドしか持たないクラス。 インターフェースからオブジェクトを作成することはできない。(implements で実装する。) 処理の再利用をしたいなら抽象クラス、クラス仕様の型定義をしたいならインターフェースを使うといい。…が、議論は多い。 ・オブジェクトのコピー 浅いコピー(shallow copy) ... オブジェクトは参照コピーされるので、コピー元のオブジェクトを変更するとコピー先のオブジェクトも変更される。 深いコピー(deep copy) ... コピー元のオブジェクトを変更しても、コピー先のオブジェクトは変更されない。 PHP5では「clone」を使うことで深いコピーができ、コピーされた時の動作は「__clone」メソッドで決定できる。 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ■Template Method ... 処理を穴埋めする 定義: アルゴリズムの構造を変えずに、アルゴリズム内のステップをサブクラスで定義する。 詳細: 似たような処理をたくさん書くとき、似たクラスを安易に量産すると、不具合を修正する際にすべてのクラスを修正する必要がある。 異なる処理の部分だけ実装できるようにすればいい。 ■Singleton ... いくつ作るかを制御する 定義: クラスのインスタンスが1つしか存在しないことを保証し、それにアクセスする方法を提供する。 詳細: データベースへの不要な複数接続を発生させたくないなど、オブジェクトを複数作らせたくない場合に有効。 ■Adapter ... APIを変更する 定義: あるクラスのインターフェースを、クライアントが求める他のインターフェースに変換する。 詳細: よくテストされた既存クラス(ShowFile)を一切変更することなく、利用側に新しい機能を提供する。 (ShowFile を修正した場合でも index.php に影響はない。必要なら DisplaySourceFile のみの修正でいい。 また、機能追加は DisplaySourceFile に行えばいい。) インターフェース統一(仕様を明確にする)のために、DisplaySourceFileInterface を通す。 ■Factory Method ... 生成処理と使用処理を分離する 定義: 処理の大枠を親クラスで規定し、具体的な処理内容をサブクラスに任せる。 詳細: オブジェクトを生成するためのAPIを定義し、オブジェクトの生成処理と使用処理を分離する。 今後他に様々な形式のデータを利用する場合やDBからデータを取得したりインターネットを通じてデータをやりとりする場合、 クライアント側のコードを変更する必要がなくなる。 ■Facade ... シンプルな唯一の窓口 定義: 複数のインターフェースに、1つの統一インターフェースを与える。 詳細: クラス同士の複雑な関係を意識しなくても利用でき、ミスが少なくなる。 ■Iterator ... 順々にアクセスする 定義: オブジェクトの内部表現を公開せずに、その要素に順にアクセスする方法を提供する。 詳細: PHP5からSPLが追加され、ArrayObjectクラス・ArrayIteratorクラス・FilterIteratorクラス・IteratorAggregateインターフェースなどが提供される。 これらを使えば、Iteratorを容易に実装できる。 PHPではforeachが暗黙的なIteratorとなり得る。 ■Abstract Factory ... 関連する部品をまとめて作る工場 定義: 互いに関連したり依存しあうオブジェクト群を、 その具象クラスを明確にせずに生成するためのインターフェースを提供する。 詳細: どのファクトリークラスを使っているのか意識する必要がなくなる。 これを使えば例えば、データベースにアクセスする本番用コードと、ダミーデータにアクセスするテスト用コードを簡単に切り替えられるようになる。 ■Bridge ... 実装と機能の架け橋 定義: 抽出されたクラスと実装を分離して、それらを独立に変更できるようにする。 詳細: 例えばデータのソートを行う場合、ソートのロジックごとにクラスを用意するとクラス数が膨大になる。 また、機能を拡張しようとするとすべてのクラスを変更する必要が出てしまい、修正作業量が膨大になる。 (だからと言って、一つのクラスですべてを補おうとすると、いつまでたってもそのクラスが安定しない。) このような場合「何をするか」と「どうやって実現するか」に分離しておくことにより、具体的な実装の追加が容易になる ■Builder ... 生成の手順と手段を分離する 定義: 複合オブジェクトについて、その作成過程を表現形式に依存しないものにすることにより、 同じ作成過程で異なる表現形式のオブジェクトを生成できるようにする。 詳細: 建築手順と材料を分けておくことにより、建築手順を簡単に入れ替えられるようにする。 例えば「URL」という材料と「RSSの解析」という手順に分離しておくことにより、URLの変更や解析方法の変更(Atomの解析に変更するなど)が容易になる。 ■Chain of Responsibility ... 処理のたらい回し 定義: 複数のオブジェクトをチェーン上につなぎ、 あるオブジェクトがその要求を処理するまで、そのチェーンに沿って要求を渡していく。 詳細: 例えば入力チェック処理をif文で実現する場合、チェックの数が多い場合や条件の内容が複雑だとコードの見通しが悪くなる。また、コードの再利用が難しくなる。 このような場合、条件と処理の組ごとに分解できると、組ごとに保守することができて再利用性が高まる。また、処理の追加や組み換えも容易になる。 具体的にはバケツリレーのように、「要求」が入ったバケツを受け取り、自分自身が処理できない場合は次の人にバケツを渡す。という手順で行う。 ■Command ... 要求をクラスで表す 定義: 要求をオブジェクトとしてカプセル化することによって、 様々な要求または要求からなるキューやログによりクライアントをパラメータ化する。 そして、取り消し可能な操作をサポートする。 詳細: 異なる種類の要求に対する処理を、同じAPIを持つクラスとして実装する。 その結果、処理クラスのインスタンスを切り替えるだけで、様々な要求に対する処理を実行できるようになる。 新しい要求に対する処理クラスを実装すれば、既存のクラスを修正する必要もない。 ■Composite ... 木構造を表す 定義: 部分-全体階層を表現するために、オブジェクトを木構造に組み立てる。 クライアントは、個々のオブジェクトとオブジェクトを合成したものを一様に扱うことができるようになる。 詳細: 単一のオブジェクトとその集合体のどちらも同じように扱えるようにする。 同じインターフェース(OrganizationEntryAbstract)を実装することにより、内部ではその差を意識すること無く扱えるようにする。 ■Decorator ... かぶせて機能UP 定義: オブジェクトに責任を動的に追加する。 サブクラス化よりも柔軟な拡張方法を提供する。 詳細: オブジェクトに対して、機能を柔軟に追加したり取り外したりできるようにする。 クラス階層の上層で機能を定義する必要がなくなるため、 再度編集する際に上層のクラスに手を入れる必要がなくなり、メンテナンス性が向上する。 ■Flyweight ... 同じものは一度しか作らない 定義: 多数の細かいオブジェクトを効率よくサポートするために共有を利用する。 詳細: 一度保持したオブジェクトが再度必要になったとき、新たに生成せずに保持したオブジェクトを返す。 オブジェクトを使い回すことで使用メモリを節約するとともに、生成にかかる時間も節約する。 ■Interpreter ... 言語の文法表現を通訳する 定義: 言語に対して、文法表現とそれをしようして文を解釈するインタプリタを一緒に定義する。 詳細: ミニ言語を実装する場合に適用されるパターン。 「1つの規則を1つのクラスで表す」ため規則の追加や変更が容易。 ここでは一例として、以下の仕様で言語を作成する。 ・「begin」という文字列で始まる。(JobCommand) ・「end」という文字列で終わる。(CommandList) ・コマンドは「diskspace」「date」「line」のいずれか。(Command) ・コマンド一覧はスペース区切りで0個以上のコマンドで構成される。(Context) ■Mediator ... すべては相談役が知っている 定義: オブジェクト群の相互作用をカプセル化するオブジェクトを定義する。 オブジェクト同士がお互いを明示的に参照しあうことがないようにして、結合度を低めることを促進する。 それにより、オブジェクトの相互作用を独立に変えることができるようになる。 詳細: オブジェクト同士の複雑なやりとりを一元管理する。 他のオブジェクトとのやりとりは必ず仲介者を通じて行うことにより、 オブジェクトは「どのオブジェクトのどのメソッドを呼び出す」といった難しいことを考える必要がなくなり、管理が容易になる。 ■Memento ... スナップショットを取る 定義: カプセル化を破壊せずに、オブジェクトの内部状態を捉えて外面化しておき、 オブジェクトを後にこの状態に戻すことができる。 詳細: PHPには「特定のクラスだけにアクセスを許可する」という制御構造がないため、本来のMementoパターンの構造を変更する必要がある。 (継承とprotectedメソッドを組み合わせることにより、DataSnapshotオブジェクトのメソッドにはDataクラス以外からアクセスできないようにする。) ■Observer ... 状態変化を通知する 定義: あるオブジェクトが状態を変えたときに、それに依存するすべてのオブジェクトに自動的にそのことが知らされ、 また、それらが更新されるように、 オブジェクト間に一対多の依存関係を定義する。 詳細: 複数のオブジェクトを連携させて動作させる場合、「このオブジェクトが変化したらこうする」というコードを書けば実現できる。 しかし、特定のクラスに依存したコードを記述するとクラス同士の関係が緊密になってしまい、クラスの再利用性が下がる。 また連携するオブジェクトの数が多い場合、管理が難しくなってしまう。 このような場合、「状態が変化したことを通知するクラス」と「通知を元にどういった処理をするか」のクラスを分けることにより、 クラスを簡単に差し替えたり追加したりできるようになる。 ■Prototype ... コピーして作る 定義: 生成すべきオブジェクトの種類を原型となるインスタンスを使って明確にし、 それをコピーすることで新たなオブジェクトの生成を行う。 詳細: 原型となるインスタンスをコピーして、新しいインスタンスを生成する。 浅いコピーなのか深いコピーなのかを、意識して実装する必要があるので注意。 ■Proxy ... 具体的な実装を隠す身代わり 定義: オブジェクトへのアクセスを制御するために、 そのオブジェクトの代理、または入れ物を提供する。 詳細: やりとりをするオブジェクトの間にクッション役を用意し、そのクッションに色々な役割を持たせる。 データベースを扱うプログラムでデータベースがなくても開発できるようにしたり、 データベースから取得したデータをキャッシュする機能をもたせたりすることができる。 クッション側のクラスは目的のオブジェクトと共通のAPIを持つため、Proxyオブジェクトでも目的のオブジェクトでも気にせず利用できる。 ■State ... 状態を表す 定義: オブジェクトの内部状態が変化した時に、オブジェクトが振る舞いを変えるようにする。 クラス内では振る舞いの変化を記述せずに、状態を表すオブジェクトを導入することでこれを実現する。 詳細: それぞれの状態に固有な処理がクラスにまとめて実装されるため、その状態に固有な処理に専念してコードを書くことができる。 これにより保守性を高めることができる。 新しい状態が追加された場合も、新しい状態クラスを作成するだけで対応できる。 ifやswitchで処理を分岐する場合に比べ、コードをすっきりさせることができる。 ■Strategy ... 戦略を切り替える 定義: アルゴリズムの集合を定義し、各アルゴリズムをカプセル化して、それらを交換可能にする。 アルゴリズムを、それを利用するクライアントからは独立に変更できるようになる。 詳細: それぞれの処理をクラスとして定義する際、クライアントにアクセスさせるための共通APIを用意しておく。 これにより、具体的な実装を意識することなく共通APIで処理を実行できる。 また、処理の実行を処理クラスのオブジェクトに移譲することで、処理の切り替えができるようにする。 ■Visitor ... 要素と要素に対する操作を分離する 定義: あるオブジェクト構造上の要素で実行されるオペレーションを表現する。 オペレーションを加えるオブジェクトのクラスに変更を加えずに、 新しいオペレーションを定義できるようになる。 詳細: データ構造と操作が分離され、それぞれのコードが局所化されるため、シンプルなコードにできる。 また、それぞれのクラスの部品化を促進できる。 データ構造が変わらなければ、新しい操作を簡単に追加できるようになる。