デザインパターンのメリット †
- 再利用性の高い柔軟な設計ができるようになる
- デザインパターンの多くは再利用性の高い柔軟な設計を目指しています。多くの熟練開発者の「知恵」が 詰まったデザインパターンを知ることで、経験がまだ少ない開発者も設計のヒントを多くもらうことがで きる
- 直接デザインパターンを使わなかったとしても、デザインパターンを理解すること で「設計力」を高めていき、より良いコードが書けるようになっていくことは、技術者として成長していく楽しみにもつながる
- 共通の言葉を使って会話ができるようになる
- デザインパターンをお互いが共通理解しているチームでは、パターン名を使った会話が交わされるようになる
- オブジェクト指向が理解できる
- デザインパターンはオブジェクト指向の基本の上に成り立っています。良いデザインパターンのサンプル は、良いオブジェクト指向プログラミングのサンプルでもあります。実プロジェクトではなかなか経験できな い「凝った設計を試すこと」で、オブジェクト指向に対する視野が広がることでしょう。
- デザインパターンの 学習により、オブジェクト指向の理解が深まるという、一石二鳥の学習効果がある
GoFデザインパターン †
生成に関するパターン †
- Abstract Factory ⭐︎
- 関連する部品を生成するファクトリごと切り替える
- インスタンスの生成を専門に行うクラスを用意することで、整合性を必要とされる一連のオブジェクト群を間違いなく生成するためのパターン
- Builder
- 複雑なオブジェクトを生成する
- 「作成過程」を決定する Director と呼ばれるものと「表現形式」を決定する Builder と呼ばれるものを組み合わせることで、オブジェクトの生成をより柔軟にし、そのオブジェクトの「作成過程」をもコントロールすることができるようにするためのパターン
- Factory Method ⭐︎
- サブクラスのメソッドにインスタンスの生成方法をまかせる
- インスタンスの生成をサブクラスに行わせることで、より柔軟に生成するインスタンスを選択することが可能となります。
- Prototype
- コピーしてインスタンスを生成する
- 「プロトタイプからインスタンスを生成する」ことができるようにするためのパターン
- Singleton ⭐︎
- 生成するインスタンスを1個に制限する
- Singleton パターンは、コンストラクタを private とすることで、他クラスから新たにインスタンスが生成されないような構造とすることで、インスタンスの生成を制御します。
構造に関するパターン †
- Adapter ⭐︎
- インタフェースが一致しないクラスを再利用する
- インタフェースに互換性の無いクラス同士を組み合わせることを目的としたパターン
- Composite ⭐︎⭐︎⭐︎
- 再帰的なオブジェクト構造を表現する
- コンポジットパターンは、要素であるオブジェクトと、複数の要素からなる複合オブジェクトを区別なく扱えるという特徴を持ちます。 この特徴を利用することで、構造を再帰的に組み立て、クライアントからの見た目をシンプルに保つことができます。
- コンポジットパターンは、任意の要素から階層を辿りすべての要素に処理を実行することを得意としています
- ディレクトリ構造のような再帰的な構造を解決することに適しています。
- 再帰的とは、ディレクトリの下にさらにディレクトリを置くことができるように、自分自身と同じタイプの要素を含むことができる性質のことです。 このような構造では、要素の下にさらに要素を含むことができる複合要素と、末端となる単独要素の2つのタイプの要素から成り立っています。 ディレクトリ構造で言うと、ディレクトリが複合要素、ファイルが単独要素に当たります。 この複合要素と単独要素を組み合わせていくことで再帰的な構造が形成されていきます。
- 再帰的な構造全体に対する処理を行いたい場合に、複合要素と単独要素を区別して実装してしまうと、処理の実装は非常に煩雑になってしまいますし、新しい要素を加えたいなどの構造に対する変更もコストが大きくなってしまいます。
- これに対しコンポジットパターンでは、すべての要素に共通の処理(オペレーション)を持つ抽象クラス(コンポーネント)を定義し、サブクラスとして複合要素(コンポジット)と単独要素(リーフ)を別々に用意しておきます。
- コンポジットクラスのオペレーションではリーフクラスのオペレーションをすべて代表して実行するように実装しておくことで、複数の要素の集まりを、まるで1つの要素であるかのように扱えます
- コンポジットパターンはクライアントから見てわかりやすい
- すべてのオブジェクトは共通の抽象クラスを持っていますので、クライアントから見て、どれがリーフなのかコンポジットなのかを判別する必要なく、どのオブジェクトも一様に扱うことができます。
- また、オブジェクト構造上の、あるリーフオブジェクトをコンポジットオブジェクトに置き換たり、新しいリーフクラスやコンポジットクラスを追加するなど構造に変化がある場合でも、コンポーネントクラスのインタフェースが変わらなければ、クライアントの処理には影響しません。
- 共通インタフェースの重要性
- コンポジットパターンでは、どのような視点で構造を抽象化するか、ということが特に重要になります。
- このパターンに登場するコンポーネントクラスはすべてのオブジェクトに共通するインタフェースを持つので、あとからインタフェースに変更を加えたくなっても、それは非常に困難になることが予想されます。 あらかじめ構造をきちんとモデル化してあげる必要があるでしょう。
- 出番の多いパターン
- アプリケーションを作っていると、コンポジットパターンが適用できそうな再帰的構造を持つデータに出会う機会は非常に多いと言えます。 身近なところではファイルとフォルダの関係がそうですし、XMLで表現されるような構造を持つデータは、コンポジットパターンが適用できる好例です。
- 膨大な規模のデータ構造を目の当たりにすると、とかく複雑に考えがちですが、構成要素をよく観察し、コンポジットパターンが適用できないか一度考えてみましょう。
- Decorator ⭐︎
- 元になるオブジェクトを包み込んで機能を拡張する
- Composite パターンを補完するパターンですので、これも適用する機会が多い
- Facade ⭐︎⭐︎
- 複雑な処理を呼び出すシンプルな入り口を提供する
- Factory パターンをはじめ、他のパターンと組み合わせることで、強力なパワーを簡単に活用できるようになります。
- ファサードパターンの目的は複雑なクラス・API呼び出しを実行するための、シンプルな入り口を用意すること
- サービスインタフェースを作ることのメリット
- ルールの明確化 ... アクションクラスからの業務処理の呼び出しはサービスインタフェース経由でのみ行うこと
- モックオブジェクトが使える ... テスト用の仮の実装を持たせたオブジェクトを用意することで、実装を待たずにアクションクラスや画面の実装・テストができるようになる
- Flyweight
- インスタンスを共有して、インスタンスの生成コスト・使用メモリを抑える
- 同じインスタンスを共有することで、無駄なインスタンスを生成しないようにして、 プログラム全体を軽くすることを目的としたパターン
- Proxy ⭐︎
- 代理(プロキシ)を用意してインスタンスの生成やアクセス制限をコントロールする
振る舞いに関するパターン †
- Chain of Responsibility
- 処理を順番にたらいまわす
- 「責任者」を「鎖状」につないでおき、「いずれかの段階」で、「誰か」が処理をすることを表現するようなパターンです。
- Command ⭐︎
- 命令そのものをオブジェクトとして扱う
- あるオブジェクトに対して要求を送るということは、そのオブジェクトのメソッドを呼び出すことと同じです。 そして、メソッドにどのような引数を渡すか、ということによって要求の内容は表現されます。さまざまな要求を送ろうとすると、引数の数や種類を増やさなければなりませんが、 それには限界があります。そこで要求自体をオブジェクトにしてしまい、そのオブジェクトを引数に渡すようにします。それがCommandパターンです。
- Strategy ⭐︎
- アルゴリズムを交換可能にする
- 継承を使用してアルゴリズム処理の差分を実装しているような処理や、switch文を多用しているようなクラスが存在する場合は、ストラテジパターンの採用を検討したほうがよいでしょう
- Template Method ⭐︎⭐︎⭐︎
- 一連の処理の一部をサブクラスで実装し、変更可能とする
- フレームワークの開発で良く利用される
- 最も基本的な、継承とポリモーフィズムの活用法を示しています。
- Visitor ⭐︎
- 複数のオブジェクトを渡り歩く処理を追加・変更する
- Composite パターンで構成されたオブジェクトを走査する強力な手段
- 「構造から処理を分離する」パターン
- 処理クラスは構造内のオブジェクトを訪問(visit)し、処理を実行して回ります。 処理を受ける側のクラスは処理が来る玄関を作っておくだけで、あとは訪問を待てばよいのです。このように処理を構造から切り分けることで、処理は処理クラスの中に閉じ込め、処理の追加や変更に対する自由度を高めることができます。