デザインパターン (ソフトウェア)
ソフトウェア開発におけるデザインパターンまたは設計パターン(英: design pattern)とは、過去のソフトウェア設計者が発見し編み出した設計ノウハウを蓄積し、名前をつけ、再利用しやすいように特定の規約に従ってカタログ化したものである。パターン(pattern)とは、型紙(かたがみ)やひな形を意味する。
本稿でのデザインは狭義の設計という意味であり、CSSやHTMLなどで使われる意匠デザインの定形を示す「デザインパターン」とは異なる。
概要
[編集]書籍『オブジェクト指向における再利用のためのデザインパターン』において、GoF (Gang of Four) と呼ばれる4人の共著者は、デザインパターンという用語を初めてソフトウェア開発に導入した。GoFは、エーリヒ・ガンマ、リチャード・ヘルム、ラルフ・ジョンソン、ジョン・ブリシディースの4人である。彼らは、その書籍の中で23種類のパターンを取り上げた。
彼らはこう述べている。
「 | [Design patterns] solve specific design problems and make object-oriented designs more flexible, elegant, and ultimately reusable. They help designers reuse successful designs by basing new designs on prior experience. A designer who is familiar with such patterns can apply them immediately to design problems without having to rediscover them. | 」 |
コンピュータのプログラミングで、素人と達人の間では驚くほどの生産性の差があり、その差はかなりの部分が経験の違いからきている。達人は、さまざまな難局を、何度も何度も耐え忍んで乗り切ってきている。そのような達人たちが同じ問題に取り組んだ場合、典型的にはみな同じパターンの解決策に辿り着く。これがデザインパターンである (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]。
脚注
[編集]- ^ a b c d McConnell, Steve (June 2004). “Design in Construction”. Code Complete (2nd ed.). Microsoft Press. p. 104. ISBN 978-0-7356-1967-8. "Table 5.1 Popular Design Patterns"
- ^ Lock Pattern
- ^ Norvig, Peter (1998). Design Patterns in Dynamic Languages.
参考文献
[編集]- エリック・ガンマ、ラルフ・ジョンソン、リチャード・ヘルム、ジョン・ブリシディース(著)、グラディ・ブーチ(まえがき)、本位田真一、吉田和樹(監訳)、『オブジェクト指向における再利用のためのデザインパターン』、ソフトバンクパブリッシング、1995。ISBN 978-4797311129.