オブジェクト指向におけるデザインパターン
Last modified: 2014-01-17
ここでは Pree の著書の論旨に基づきつつ、他の文献からの引用をおりまぜて、
「デザインパターン」の意義と概略を整理してみたいと思います。
あくまでも私の解釈と経験に基づく理解なので、誤りや説明不足はご容赦ください。
オリジナルの文献を手に取るきっかけとなれば幸いです。
文献
- 特集「デザインパターンとオブジェクト指向設計」
月刊ドクター・ドブズ・ジャーナル日本版, 1997年10月号.
- Design Patterns
Elements of Reusable Object-Oriented Software,
by Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides,
Addison-Wesley Professional Computing Series,
0-201-63361-2 * Hardcover * 416 pages *1995.
- Design Patterns CD
Elements of Reusable Object-Oriented Software,
Erich Gamma, Richard Helm, Ralph Johnson and John Vlissides,
ISBN 0-201-63498-8 * CD * 1997.
- 「オブジェクト指向における再利用のためのデザインパターン」
Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides,
本位田真一,吉田和樹監訳,
ソフトバンク,1995.
- Design Pattern for Object-Oriented Software Development,
Wolfgang Pree, ACM Press, 1995.
邦訳「デザインパターンプログラミング」
アジソンウェスレイトッパン情報科学シリーズ77,
佐藤啓太,金沢典子訳, トッパン, 1996.
■デザインパターンについて
- 既存のオブジェクト指向分析/設計方法論は充分ではない。
実装ではなく、設計を再利用すべき。
-
簡単にいえば、オブジェクト指向の
のうち、従来のOOAD(オブジェクト指向分析/設計)手法においては
「設計」の部分が手薄であったと考え、ここにもっと力を注げば実装が楽になる、
と主張しているようです。
-
オブジェクト指向の入門書を読むと、親クラスとか子クラスとかの説明として、
ほ乳類と猫、とか、人間と学生、みたいな例が多く見られます。
私の経験では、
特定の問題領域をオブジェクト指向
でシステム化するときに、データの継承関係を思いつくまま書き並べて、
それをそのまま個々のクラスとして実装する、というやり方では、
あるところからコーディングがやりにくくなって、結局途中で
クラス設計の見直しが必要になります。
これは「属性が継承されているクラスを親子にすれば、実装が効率的になる」
というお題目を盲目的に信じてしまったからであり、実際には
「どんなメソッドを共有すれば、何が実装時に改善されるのか」というメリットを
合理的に判断しながら分析を行う必要があると思います。
そしてアルゴリズムの設計や実装におけるオブジェクト指向のメリットを
見極めるためには、デザインパターンという視点が有用かなと思います。
- 再利用可能なフレームワークはいかに設計するべきか?
現状では、さまざまなフレームワークを勉強し、共通する
デザインパターンを抽出することでしか学べない。
- 「フレームワーク」とは、アプリケーションの
ひな形の役割を持つクラスライブラリ、という意味です。
Visual C++ に添付される MFC や、Macintosh のプログラムで
用いられる MacApp などがフレームワークの例です。
■デザインパターンを明示することのメリット
- フレームワークの設計思想を知れば、それを使って
アプリケーションを書くのに役立つ。
- 既存のフレームワークの設計が理解できれば、その
経験を新しいフレームワークの開発に活かせる。
■デザインパターンに関する既存の研究
- オブジェクト指向パターン [Cord92]
SmallTalk MVC モデルをさらに抽象化することができた。
- コーディングパターン [Coplien92]
標準的なソースコード構造とネーミングの基準を打ち立てた。
また、特に C++ では言語の欠点を回避するために重要。
特定の言語に依存。
- アプリケーションフレームワークのクックブック
フレームワークの使い方のレシピの集合。
ようするに MFC のサンプルソースコードみたいなものですね。
- 形式契約
相互依存するオブジェクトの協調に関する形式記法。
- デザインパターンカタログ [Gammaら]
「全体的なシステムアーキテクチャを概観するのに役立つミクロアー
キテクチャ(クラスとクラス間の関係)の再利用性」
「設計の共通ボキャブラリを形成する」
「フレームワークは、プログラミング言語で実装される。この意味
で、フレームワークはデザインパターンより具象的である。成熟し
たフレームワークは、デザインパターンを再利用したものになって
いる」
Gamma らは 20 個以上のデザインパターンカタログを提案。
しかし、カタログの多くのパターンが類似したものである。
■デザインパターンカタログ
Gammaらの「オブジェクト指向における再利用のためのデザインパターン」
は「即効性のある23のパターンカタログ集」です。
現実の問題に当てはめやすく、個々のパターンの実装における
トレードオフのポイントなども解説されています。
忙しい人は Pree を読む前に Gamma らを読むとよいと思います。
また、
DDJ日本版1997/10 の特集は、C++ の最新の仕様(STL など)を用いていて実践的です。
Delphi のライブラリで使われている Windows GUI のためのデザインパターンも
解説されています。
いずれにせよ、レイトバインディングを使うことで、手続き指向に
おける「分岐構文」を隠蔽する、というのが基本概念ですね。。
たとえば、State というパターンの例では、
class TCPState {
virtual Open() = 0;
virtual Close() = 0;
virtual Acknowledge() = 0;
} ;
class TCPEstablished : public TCPState {
Open() ;
Close();
Acknowledge();
} ;
class TCPListen : public TCPState {
Open() ;
Close();
Acknowledge();
} ;
みたいになっていて、
class TCPConnection {
// これは TCPState に対する Context クラス
TCPState *pState ;
Open();
} ;
のように現在の状態に応じたオブジェクトを入れておく。
TCPConnection::Open()
{
pState->Open();
}
すれば、常に現在適切なメソッドが選ばれる。
「実行状態」をオブジェクトにすることにより、条件分岐をより構
造化できる、という感じです。
同じ考え方で正規表現のパーザーを表現したものもあり、
Interpreter パターンという名前がついています。
あと、MFC で CWinApp を継承することはどのデザインパターンに
あてはまるかとか、ダイアログリソースごとに CMyDialog を実装
するとはどのパターンだとか、JDK1.0 から 1.1 になってイベント
処理のデザインパターンはどれにかわったとか、そういうことが、
なんとなくわかってきたような気がします。
さて、自分の仕事が Gamma のカタログでは解決しないと感じた場合は、
Pree のメタパターンを学んでみる価値があるでしょう。
乱暴にいえば、Pree の「メタパターン」は、仮想関数と継承を
使ってやれることを数学的に単純化したものであり、
Gamma らの「デザインパターン」は、それらの具体的な応用を示し
たもの、という感じがします。
■メタパターン
Pree は、デザインパターンを「メタパターン」に抽象化。
よいフレームワークは適切な「ホットスポット」を持つ。
(ホットスポット:可変場所)
フレームワークの実装においては
- 可変箇所(ホットスポット) =フックメソッド
- 固定個所(コールドスポット)=テンプレートメソッド
として実装する。
フックメソッドは下の3つのどれかである。
- 抽象メソッド
- 正規メソッド = 他のメソッドを呼び出さない実装
- テンプレートメソッド
テンプレートとフックの関係は相対的なものとなる。
■クラスとして実装されるテンプレートとフック
Pree はクラスの組合せ方として7つのメタパターンを提案。
- 統合 (Unification)
- 1:1結合 (1:1 Connection)
- 1:N結合 (1:N Connection)
- 1:1 再帰的結合 (1:1 Recursive Connection)
- 1:1 再帰的統合 (1:1 Recursive Unification)
- 1:N 再帰的結合 (1:N Recursive Connection)
- 1:N 再帰的統合 (1:N Recursive Unification)
Pree の本の後半では具体的な実装を元に上記のパターンを検証。
さきほどの State や Interpreter などの命名は E.Gamma らの仕事ですが、これらを
W.Pree 流にまとめると 1:N 結合の例になります。
- 1:N 結合 = TCPConnection と TCPState
- Template = TCPConnection::Open()
Hook = TCPState::Open()
ということですね。
Pree の「デザインパターンプログラミング」には
パターンの実装における具体的な判断の
基準が明記されていて気持ちよいです。
たとえば p.152 にはこうまとめられています。
- 1:N 再帰的結合パターン
特別なオブジェクトだけが他のオブジェクトを持つことができる
- 1:N 再帰的統合パターン
どのオブジェクトも他のオブジェクトを持つことができる
たとえば Composite パターンをどちらで実装すればよいかまよったら、
自分が適応したいドメインにおいて、オーバライドする数を
より少なくすませられる方を選べばよい、ということだと思います。
2014-01-17 文献リストのスペルミスを修正ました。
nishimoto [atmark] m.ieice.org / Takuya NISHIMOTO