デザインパターン (ソフトウェア)
ソフトウェア開発におけるデザインパターン(型紙(かたがみ)または設計パターン、英: design pattern)とは、過去のソフトウェア設計者が発見し編み出した設計ノウハウを蓄積し、名前をつけ、再利用しやすいように特定の規約に従ってカタログ化したものである。
目次
概要
書籍『オブジェクト指向における再利用のためのデザインパターン』において、GoF (Gang of Four; 四人組) と呼ばれる4人の共著者は、デザインパターンという用語を初めてソフトウェア開発に導入した。GoFは、エーリヒ・ガンマ、リチャード・ヘルム、ラルフ・ジョンソン、ジョン・ブリシディースの4人である。彼らは、その書籍の中で23種類のパターンを取り上げた。
彼らはこう述べている。 テンプレート:Cquote
コンピュータのプログラミングで、素人と達人の間ではびっくりするほどの生産性の差があるが、その差はかなりの部分が経験の違いからきている。達人は、さまざまな難局を、何度も何度も耐え忍んで乗り切ってきている。そのような達人たちが同じ問題に取り組んだ場合、典型的にはみな同じパターンの解決策にたどり着くのだが、これがデザインパターンである (GoF)。
それぞれのパターンは、プログラマの間で何度も繰り返し考え出されてきた。したがって、それは最善の解決策ではないかもしれないが、その種の問題に対するトレードオフも考慮した、典型的な解決策ではある。更に、コストがかかるかもしれない問題解決を実際に行う前の先行調査として大変役に立つ。パターンには名前がついていることが重要である。なぜなら、名前がついていることで問題や解決策を記述したり、会話の中で取り上げたりすることができるようになるからである。
主要なデザインパターンの一覧
生成に関するパターン
パターン名 | 概要 | GoF | Code Complete[1] |
---|---|---|---|
Abstract Factory | 関連する一連のインスタンスを状況に応じて適切に生成する方法を提供する。 | テンプレート:Yes | テンプレート:Yes |
Builder | 複合化されたインスタンスの生成過程を隠蔽する。 | テンプレート:Yes | テンプレート:No |
Factory Method | 実際に生成されるインスタンスに依存しない、インスタンスの生成方法を提供する。 | テンプレート:Yes | テンプレート:Yes |
Prototype | 同様のインスタンスを生成するために、原型のインスタンスを複製する。 | テンプレート:Yes | テンプレート:No |
Singleton | あるクラスについて、インスタンスが単一であることを保証する。 | テンプレート:Yes | テンプレート:Yes |
構造に関するパターン
パターン名 | 概要 | GoF | Code Complete[1] |
---|---|---|---|
Adapter | 元々関連性のない2つのクラスを接続するクラスを作る。 | テンプレート:Yes | テンプレート:Yes |
Bridge | クラスなどの実装と、呼出し側の間の橋渡しをするクラスを用意し、実装を隠蔽する。 | テンプレート:Yes | テンプレート:Yes |
Composite | 再帰的な構造を表現する。 | テンプレート:Yes | テンプレート:Yes |
Decorator | あるインスタンスに対し、動的に付加機能を追加する。Filterとも呼ばれる。 | テンプレート:Yes | テンプレート:Yes |
Facade | 複数のサブシステムの窓口となる共通のインタフェースを提供する。 | テンプレート:Yes | テンプレート:Yes |
Flyweight | 多数のインスタンスを共有し、インスタンスの構築のための負荷を減らす。 | テンプレート:Yes | テンプレート:No |
Proxy | 共通のインタフェースをもつインスタンスを内包し、利用者からのアクセスを代理する。Wrapperとも呼ばれる。 | テンプレート:Yes | テンプレート:No |
振る舞いに関するパターン
パターン名 | 概要 | GoF | Code Complete[1] |
---|---|---|---|
Chain of Responsibility | イベントの送受信を行う複数のオブジェクトを鎖状につなぎ、それらの間をイベントが渡されてゆくようにする。 | テンプレート:Yes | テンプレート:No |
Command | 複数の異なる操作について、それぞれに対応するオブジェクトを用意し、オブジェクトを切り替えることで操作の切替えを実現する。 | テンプレート:Yes | テンプレート:No |
Interpreter | 構文解析のために、文法規則を反映するクラス構造を作る。 | テンプレート:Yes | テンプレート:No |
Iterator | 複数の要素を内包するオブジェクトのすべての要素に順にアクセスする方法を提供する。反復子。 | テンプレート:Yes | テンプレート:Yes |
Mediator | オブジェクト間の相互作用を仲介するオブジェクトを定義し、オブジェクト間の結合度を低くする。 | テンプレート:Yes | テンプレート:No |
Memento | データ構造に対する一連の操作のそれぞれを記録しておき、以前の状態の復帰または操作の再現が行えるようにする。 | テンプレート:Yes | テンプレート:No |
Observer (出版-購読型モデル) | インスタンスの変化を他のインスタンスから監視できるようにする。Listenerとも呼ばれる。 | テンプレート:Yes | テンプレート:Yes |
State | オブジェクトの状態を変化させることで、処理内容を変えられるようにする。 | テンプレート:Yes | テンプレート:No |
Strategy | データ構造に対して適用する一連のアルゴリズムをカプセル化し、アルゴリズムの切替えを容易にする。 | テンプレート:Yes | テンプレート:Yes |
Template Method | あるアルゴリズムの途中経過で必要な処理を抽象メソッドに委ね、その実装を変えることで処理が変えられるようにする。 | テンプレート:Yes | テンプレート:Yes |
Visitor | データ構造を保持するクラスと、それに対して処理を行うクラスを分離する。 | テンプレート:Yes | テンプレート:No |
マルチスレッドプログラミングに関するパターン
マルチスレッドプログラミングは難易度が比較的高いとされる。そのため、よく現われる問題に対して汎用的に使用できる種々のパターンが考案されている。
パターン名 | 概要 |
---|---|
Active Object (Actor) | メソッドの呼び出しとメソッドの実際の実行を分離することで並行性を導入する。各オブジェクトは利用者からの要求を管理するためのメッセージキューとスケジューラを持つ。 |
Balking | 前提条件が満たされていない場合は、(その時点での)処理の実行をあきらめる。 |
Double-checked locking | ロックの取得におけるオーバヘッドを削減するための技法。まずをスレッドセーフでない方法で「ロックヒント」を調べて、それが成功したら実際のロックを試みる。 |
Future | 「処理が完了しているかどうか分からない処理結果」を表すオブジェクトを作成することで同期を実現する。処理が完了していないうちに結果を取得しようとした場合は処理が完了するまでロックされる。 |
Guarded suspension | 前提条件が満たされるまで待機するための機構。 |
Lock | リソースに対して1つのスレッドが「ロック」をかけて、そのあいだ他のスレッドがそのリソースにアクセスしたり変更を加えたりできないようにする。[2] |
Monitor | 排他的に実行しなければならないメソッド群を持つオブジェクトをスレッドセーフに利用できるようにするための機構。Javaはこれを言語レベルでサポートしている。 |
Producer-consumer | 「生産者」 (producer) スレッド群がデータを生成して「通信路」に追加し、「消費者」 (consumer) スレッド群がそのデータを「通信路」から取り出して処理するという構造。必要な同期はすべて「通信路」によって行なわれるため、生産者と消費者のルーチンは同期を意識せずに実装できる。この通信路は同期キューなどで実現される(一部の言語はこれを標準ライブラリで提供している)。 |
Reactor | 同期的に扱わなければならないリソース群に対する非同期的インタフェースを提供する。 |
Readers-writer lock | 書き込みは排他アクセスが必要だが読み込みは並行に行えるようにしたい場合のためのロック機構。 |
Scheduler | シングルスレッドで実行される処理(例えばファイルへの書き込み)の実行を各スレッドに許可するタイミングを明確に制御する。 |
Thread pool | 多数のスレッドを作成してそれらに多数のタスクを処理させる。典型的な状況ではスレッド数よりもかなり多くのタスクが存在し、各スレッドは、あるタスクの処理が終わると次の処理待ちタスクの実行に取りかかる。一般に、Producer-consumerパターンを使って実現される。 |
Thread-specific storage | 静的変数・グローバル変数のように扱えるがスレッドごとに異なる内容を格納できるメモリ領域を提供する。 |
Two-phase termination | スレッドを安全に終了させる方法。スレッドは、終了要求を表すフラグを定期的に確認して、それがセットされたら終了処理を行う。 |
注意および批判
デザインパターンは、よく使われる設計を一般化された形でまとめたものに過ぎない。具体的な実装を提供するものではなく、あくまでもコンセプトとして参照されることが意図されている。サンプルコードは実装例に過ぎない。
デザインパターンはすべての状況における最善の設計では決してない。「Code Complete」は、デザインパターンを紹介している書籍の1つであるが、デザインパターンをむやみに適用するのは不適切であり、不適切な使用はコードの複雑さを無意味に高めてしまうと注意している。[1]
一部のデザインパターンは、プログラミング言語(例: Java, C++)の機能の欠損の印であると主張されることがある。計算機科学者のピーター・ノーヴィグは、GoFによるデザインパターン本の23パターンのうち16パターンは、言語によるサポートによって単純化または除去できることをLispやDylanを用いて実演した。[3]
参考文献
- エリック・ガンマ、ラルフ・ジョンソン、リチャード・ヘルム、ジョン・ブリシディース(著)、グラディ・ブーチ(まえがき)、本位田真一、吉田和樹(監訳)、『オブジェクト指向における再利用のためのデザインパターン』、ソフトバンクパブリッシング、1995。ISBN 978-4797311129.