ソフトウェア開発におけるデザインパターン(設計パターン、)とは、ソフトウェア設計に繰り返し現れる問題に対する実績ある解決方法を、適用状況と解決構造の関係として整理し、名称とともに再利用可能な形で共有するためにまとめられた設計知識である。

個別の実装ではなく設計上の構造を共有することで、開発効率と設計品質の向上を図ることを目的とする。本稿でいう「デザイン」は(ソフトウェアの)設計を指し、意匠や画面構成を指す「デザイン」ではない。

概要 書籍『オブジェクト指向における再利用のためのデザインパターン』において、GoF (Gang of Four) と呼ばれる4人の共著者は、デザインパターンという用語を初めてソフトウェア開発に導入した。GoFは、エーリヒ・ガンマ、リチャード・ヘルム、ラルフ・ジョンソン、ジョン・ブリシディースの4人である。彼らは、その書籍の中で23種類のパターンを取り上げた。

彼らはこう述べている。

コンピュータのプログラミングで、素人と達人の間では驚くほどの生産性の差があり、その差はかなりの部分が経験の違いからきている。達人は、さまざまな難局を、何度も何度も耐え忍んで乗り切ってきている。そのような達人たちが同じ問題に取り組んだ場合、典型的にはみな同じパターンの解決策に辿り着く。これがデザインパターンである (GoF)。

それぞれのパターンは、プログラマの間で何度も繰り返し考え出されてきた。したがって、それは最善の解決策ではないかもしれないが、その種の問題に対するトレードオフを考慮した、典型的な解決策ではある。さらに、コストがかかるかもしれない問題解決を実際に行う前の先行調査として、大変役に立つ。パターンに名前が付いていることが重要である。なぜなら、名前が付いていることで問題や解決策を記述したり、会話の中で取り上げたりすることができるようになるからである。

主要なデザインパターンの一覧 生成に関するパターン パターン名 概要GoFCode CompleteAbstract Factory関連する一連のインスタンスを状況に応じて、適切に生成する方法を提供する。Builder複合化されたインスタンスの生成過程を隠蔽する。Factory Method実際に生成されるインスタンスに依存しない、インスタンスの生成方法を提供する。Prototype同様のインスタンスを生成するために、原型のインスタンスを複製する。Singletonあるクラスについて、インスタンスが単一であることを保証する。

構造に関するパターン パターン名 概要GoFCode CompleteAdapter元々関連性のない2つのクラスを接続するクラスを作る。Bridgeクラスなどの実装と、呼び出し側の間の橋渡しをするクラスを用意し、実装を隠蔽する。Composite再帰的な構造を表現する。Decoratorあるインスタンスに対し、動的に付加機能を追加する。Filterとも呼ばれる。Facade複数のサブシステムの窓口となる共通のインタフェースを提供する。Flyweight多数のインスタンスを共有し、インスタンスの構築のための負荷を減らす。Proxy共通のインタフェースを持つインスタンスを内包し、利用者からのアクセスを代理する。Wrapperとも呼ばれる。

振る舞いに関するパターン パターン名 概要GoFCode CompleteChain of Responsibilityイベントの送受信を行う複数のオブジェクトを鎖状につなぎ、それらの間をイベントが渡されていくようにする。Command複数の異なる操作について、それぞれに対応するオブジェクトを用意し、オブジェクトを切り替えることで、操作の切り替えを実現する。Interpreter構文解析のために、文法規則を反映するクラス構造を作る。Iterator複数の要素を内包するオブジェクトのすべての要素に対して、順番にアクセスする方法を提供する。反復子。Mediatorオブジェクト間の相互作用を仲介するオブジェクトを定義し、オブジェクト間の結合度を低くする。Mementoデータ構造に対する一連の操作のそれぞれを記録しておき、以前の状態の復帰または操作の再現が行えるようにする。Observer (出版-購読型モデル)インスタンスの変化を他のインスタンスから監視できるようにする。Listenerとも呼ばれる。Stateオブジェクトの状態を変化させることで、処理内容を変えられるようにする。Strategyデータ構造に対して適用する一連のアルゴリズムをカプセル化し、アルゴリズムの切り替えを容易にする。Template Methodあるアルゴリズムの途中経過で必要な処理を抽象メソッドに委ね、その実装を変えることで処理内容を変えられるようにする。Visitorデータ構造を保持するクラスと、それに対して処理を行うクラスを分離する。

マルチスレッドプログラミングに関するパターン ソフトウェアに並行性・並列性を導入するためのマルチスレッドプログラミングは、難易度が比較的高いとされる。そのため、よく現れる問題に対して、汎用的に使用できる種々のパターンが考案されている。後発の高水準プログラミング言語や、並行・並列プログラミングのサポートを重視した言語では、標準ライブラリや言語構文・言語仕様に取り込まれているものも多い。

パターン名 概要 (Actor) メソッドの呼び出しとメソッドの実際の実行を分離することで、並行性を導入する。各オブジェクトは、利用者からの要求を管理するためのメッセージキューとスケジューラを持つ。 前提条件が満たされていない場合は、(その時点での)処理の実行をあきらめる。 ロックの取得におけるオーバヘッドを削減するための技法。まずスレッドセーフでない方法で「ロックヒント」を調べて、それが成功したら実際のロックを試みる。 Future 「処理が完了しているかどうか分からない処理結果」を表すオブジェクトを作成することで同期を実現する。処理が完了していないうちに結果を取得しようとした場合は、処理が完了するまでロックされる。 前提条件が満たされるまで待機するための機構。 Lock リソースに対して1つのスレッドが「ロック」をかけて、その間は他のスレッドがそのリソースにアクセスしたり変更を加えたりできないようにする。Lock Pattern Monitor 排他的に実行しなければならないメソッド群を持つオブジェクトをスレッドセーフに利用できるようにするための機構。Javaは、これを言語レベルでサポートしている。 Producer-consumer 「生産者」 (producer) スレッド群がデータを生成して「通信路」に追加し、「消費者」 (consumer) スレッド群がそのデータを「通信路」から取り出して処理するという構造。必要な同期はすべて「通信路」によって行なわれるため、生産者と消費者のルーチンは同期を意識せずに実装できる。この通信路は、同期キューなどで実現される(一部の言語はこれを標準ライブラリで提供している)。 同期的に扱わなければならないリソース群に対する非同期的インタフェースを提供する。 書き込みは排他アクセスが必要だが、読み込みは並行に行えるようにしたい場合のためのロック機構。 シングルスレッドで実行される処理(例えばファイルへの書き込み)の実行を各スレッドに許可するタイミングを明確に制御する。 多数のスレッドを作成して、それらに多数のタスクを処理させる。典型的な状況ではスレッド数よりもかなり多くのタスクが存在し、各スレッドは、あるタスクの処理が終わると次の処理待ちタスクの実行に取りかかる。一般に、Producer-consumerパターンを使って実現される。 Thread-specific storage 静的変数・グローバル変数のように扱えるがスレッドごとに異なる内容を格納できるメモリ領域を提供する。 Two-phase termination スレッドを安全に終了させる方法。スレッドは、終了要求を表すフラグを定期的に確認して、それがセットされたら終了処理を行う。

注意および批判 デザインパターンは、よく使われる設計を一般化された形でまとめたものに過ぎない。そのため、具体的な実装を提供するものではなく、あくまでもコンセプトとして参照されることが意図されている。つまり、サンプルコードは、実装例に過ぎない。

デザインパターンは、すべての状況における最善の設計ではない。『Code Complete』は、デザインパターンを紹介している書籍の1つであるが、デザインパターンをむやみに適用するのは不適切であり、不適切な使用はコードの複雑さを無意味に高めてしまうと注意している。

一部のデザインパターンは、プログラミング言語(例: Java, C++)の機能の欠損の印であると主張されることがある。計算機科学者のピーター・ノーヴィグは、GoFによるデザインパターン本の23パターンのうち16パターンは、言語によるサポートによって単純化または除去できることをLispやDylanを用いて実演した。

脚注

参考文献 エリック・ガンマ、ラルフ・ジョンソン、リチャード・ヘルム、ジョン・ブリシディース(著)、グラディ・ブーチ(まえがき)、本位田真一、吉田和樹(監訳)、『オブジェクト指向における再利用のためのデザインパターン』、ソフトバンクパブリッシング、1995。.

関連項目

アンチパターン アナリシスパターン (マーティン・ファウラー) 開放/閉鎖原則