デザインパターン。それは、ソフトウェア設計における一般的な問題を解決するための設計の手法・テクニックです。簡単に言えば、「こうやると良いよ!」というパターン集のようなものです。
私はJavaを学んでいる際に知りましたが、当時は難しすぎてよく分かりませんでした。。
その後プログラミングを仕事とする上で時折「これは○○パターンだな」というものに触れてきましたが、なんとなくの理解です。人に説明できる程ではありません。
ブログを書くようになり、このような「なんとなく」の理解を改めて調べ、説明する習慣ができました。今回は、ちょっとハードルを上げて、てデザインパターンの理解を深め、アウトプットすることにします。
デザインパターンについては初心者に向けた記事が見当たらず、難しいものになりがちです。できるだけ初心者の方にも読んでいただけるように考慮して書きたいと思います。
デザインパターンについて
デザインパターンとは
1994年、GoF(Gang of Four)と称される4人の著名なプログラマによって「Design Patterns: Elements of Reusable Object-Oriented Software」という本が出版されました。
この書籍は、ソフトウェア工学とオブジェクト指向設計の分野において非常に影響力があり、デザインパターンの考え方を一般的に知らしめるきっかけとなったそうです。
約30年も前のことですが、昨今まで語り継がれているのは流行廃りのある内容ではないということ。つまりずっと使える手法・テクニックであることの証でしょう。
今回はPHPで見ていきますが、デザインパターンは特定のプログラム言語に限ったものではありません。学んでおけば、一生自分の血肉となり活躍してくれるはずです。
メリット
デザインパターンを学ぶことによって、以下の様なメリットがあります。
- 再利用性:
様々な場所で使える。 - コミュニケーション:
理解している者同士のコミュニケーションが容易になる。 - 堅牢な設計:
デザインパターンの堅牢さ、信頼度は折り紙付き(約30年~の実績)。 - 変更・拡張性:
適切に使えば、変更や拡張がしやすい。
適切に使えれば、大きなメリットがあると言えそうです。
デメリット
メリットだけではなく、一応、デメリットと言える要素もあります。
- 過度な使用:
学べば使いたくなりがち。なんでもデザインパターンはNG。 - 複雑になる:
必要以上にデザインパターンを導入すると、システムが不必要に複雑になる可能性。 - 学習コスト:
落とし込むのが難しい(私も挫折経験あり)。チームの場合は全員が理解する必要がある。 - パフォーマンス:
特定のシーンにてパフォーマンスの低下を引き起こす可能性。 - 頭が固くなる:
パターンに固定されすぎると、新しいアプローチや独自の解決策を考えるのが難しくなる場合がある。
特に、覚えたら何でもデザインパターンしたがるのは「あるある」だと思いますので要注意です。
デザインパターンは魔法のように、全ての案件で適合するものではありません。適した場所で使ってこそ、活きるものです。
新しい知識を使いたくなるのは誰もが経験があると思います。実験なら悪いことではないですけど、実務では……。
オブジェクト指向なの!?
私が最初に気になった点として、デザインパターンはオブジェクト指向なのか!?ということです。
結論から言えば、デザインパターンは必ずしもオブジェクト指向に限定されません。
多くのデザインパターンがオブジェクト指向プログラミング(OOP)の文脈で紹介され、説明されているのが現実です。
……が、関数型プログラミングや手続き型プログラミングの文脈でもあてはまるものもあります。
先ほどのとおり言語にも限定されないため、本当に幅広く、末永く使えるのがデザインパターン。言わばプログラミングの素地を底上げするものと言っても過言ではないでしょう。
デザインパターン一覧
全23種のデザインパターンの概要を一覧としてまとめました。英語での名称も付けておくので、英語が得意な方は海外の文献をチェックするのもありですね。
大きく分けて、以下の3つのグループに分けられます。
- 生成に関するパターン
- 構造に関するパターン
- 振る舞いに関するパターン
それでは見ていきましょう。
生成に関するパターン(Creational Patterns)
オブジェクトの作成方法に関連するパターンです。
オブジェクトの直接のインスタンス化を避けることで、システムの柔軟性を高めることを目的としています。
- シングルトン (Singleton)
クラスのインスタンスが1つしか生成されないことを保証するパターン。全体のシステムで共有される設定やリソースを効率的に管理することができます。 - プロトタイプ (Prototype)
既存のオブジェクトをコピーして新しいオブジェクトを作成するパターン。オブジェクトの構造が複雑な場合でも迅速にクローンを生成することが可能です。 - ファクトリーメソッド (Factory Method)
インスタンスの生成をサブクラスに委譲するパターン。システムを新しいクラスに柔軟に適応させることができます。 - 抽象ファクトリー (Abstract Factory)
関連するオブジェクトのグループを一度に作成するインターフェースを提供するパターン。系統的なオブジェクト群の生成を簡単に変更・追加することができます。 - ビルダー (Builder)
オブジェクトの複雑な構築過程を分離・抽象化するパターン。同じ作成過程で異なる表現形式のオブジェクトを生成できます。
構造に関するパターン(Structural Patterns)
オブジェクトやクラスが共同で形成する大きな構造に関するパターンです。
クラスやオブジェクトの合成を助け、予期しない関係を持つもの同士を橋渡しする役割を果たします。
- アダプター (Adapter)
既存のクラスのインターフェースを新しいインターフェースに変換するパターン。互換性のないインターフェースをもつクラスを統合して動作させることができます。 - ブリッジ (Bridge)
抽象化と実装を分離し、それらを独立して変更できるようにするパターン。抽象化と実装をそれぞれ独立に拡張することができ、柔軟性が向上します。 - コンポジット (Composite)
オブジェクトを木構造で構築し、単一オブジェクトと複合オブジェクトを同じように扱うパターン。クライアントが単一オブジェクトと複合オブジェクトを均一に扱えるので、コードの複雑さが減少します。 - デコレータ (Decorator)
オブジェクトに動的に追加的な責任を追加するパターン。
サブクラス化の代わりに柔軟な拡張を可能にします。 - ファサード (Facade)
複雑なシステムのサブシステムに対して統一されたインターフェースを提供するパターン。システムへのアクセスを簡略化し、クライアントとサブシステム間の依存を減少させます。 - フライウェイト (Flyweight)
多くの細かいオブジェクトを効率よく共有するためのパターン。メモリ消費を最小限に抑えながら、大量のオブジェクトを効果的に管理します。 - プロキシ (Proxy)
他のオブジェクトへのアクセスを制御するための代理オブジェクトを提供するパターン。アクセス制御や遅延初期化など、本来のオブジェクトへのアクセス前後で追加の処理を行うことができます。
振る舞いに関するパターン(Behavioral Patterns)
オブジェクト間のコミュニケーションや協調動作に関連するパターンです。
オブジェクト間の責任分担や通信方法を定義します。
- テンプレートメソッド (Template Method)
アルゴリズムの骨格を定義し、一部のステップをサブクラスで実装するパターン。アルゴリズムの構造を維持しながら、柔軟にステップを変更または拡張できます。 - メディエータ (Mediator)
クラス間の相互作用をカプセル化するオブジェクトを定義するパターン。クラス間の結合度を低減し、再利用性を向上させることができます。 - オブザーバー (Observer)
オブジェクトの状態が変更された際に、その変更を他のオブジェクトに自動的に通知するパターン。状態の変更を迅速に反映させることで、システムの一貫性を維持できます。 - ビジター (Visitor)
オブジェクト構造の要素に新しい操作を実行する能力を追加するパターン。オブジェクト構造と操作を分離することで、新しい操作を簡単に追加できます。 - ストラテジー (Strategy)
アルゴリズムファミリを定義し、それらを相互に交換可能にするパターン。アルゴリズムを使用するクライアントから独立してアルゴリズムを変更できます。 - ステート (State)
オブジェクトの内部状態が変わったときに、オブジェクトの振る舞いを変更するパターン。状態に依存する振る舞いを綺麗にカプセル化し、コードの重複を避けることができます。 - リスポンシビリティチェーン (Chain of Responsibility)
複数のオブジェクトがリクエストを処理できる機会を持ち、連鎖的にリクエストを送るパターン。リクエストを処理するオブジェクト間の結合度を低く保ち、柔軟性を向上させます。 - コマンド (Command)
リクエストをオブジェクトの形でカプセル化し、パラメータを使ってキューに保存したり、ログに記録するパターン。操作のキューイング、ログの取得、取り消し操作などをサポートすることができます。 - インタープリター (Interpreter)
ある言語の文法を表現するための表現方法を定義し、その文法で定義された文を解釈するインタープリタを使用するパターン。特定の種類の問題に対して簡単に言語を構築・解釈できます。 - イテレータ (Iterator)
コレクションの要素に順番にアクセスする方法を提供するパターン。コレクションの内部構造を知らずに、要素にアクセスすることができます。 - メメント (Memento)
オブジェクトの内部状態をキャプチャし、後でこの状態に戻すことができるようにするパターン。ある時点のスナップショットを取得し、後でその状態に復元することができます。
なんのこっちゃさっぱりです。
これから詳細ページをせっせと作っていくので、それを見れば理解できるようにしますね!
まとめ
デザインパターン及び、それら全23種をご紹介しました。それぞれの詳細ページにに関しては、定期的に追加していこうと思います。
全ての詳細ページを作るには年単位で時間がかかるかもしれません。ですが、私も興味が強い分野なので、自分の勉強もかねてコツコツと取り組みたいと思います。
とても大変ですが、30年も語り継がれているのには訳があるはず。きっとエンジニア人生にとってプラスになると信じてがんばります。
コメント