...

生成に関するパターン

by user

on
Category: Documents
8

views

Report

Comments

Transcript

生成に関するパターン
5
目次
序文
17
はじめに
21
Part 1 一般的に使われるパターン
Chapter 1
生成に関するパターン
1.1 概要
29
30
1.2 Abstract Factory パターン
目的
31
31
導入理由
31
パターンを適用可能な状況
32
Abstract Factory パターンとは
実装方法
32
長所と短所
33
パターンのバリエーション
関連するパターン
34
34
サンプルプログラム
1.3 Builder パターン
目的
34
38
38
導入理由
38
パターンを適用可能な状況
Builder パターンとは
実装方法
39
39
39
長所と短所
40
パターンのバリエーション
関連するパターン
1.4 Factory Method パターン
46
41
41
サンプルプログラム
目的
32
41
46
6
目次
導入理由
46
パターンを適用可能な状況
47
Factory Method パターンとは
実装方法
47
47
長所と短所
48
パターンのバリエーション
関連するパターン
49
サンプルプログラム
1.5 Prototype パターン
目的
48
49
52
52
導入理由
52
パターンを適用可能な状況
52
Prototype パターンとは
実装方法
52
53
長所と短所
53
パターンのバリエーション
関連するパターン
55
サンプルプログラム
1.6 Singleton パターン
目的
54
55
58
58
導入理由
58
パターンを適用可能な状況
59
Singleton パターンとは
実装方法
59
59
長所と短所
60
パターンのバリエーション
関連するパターン
60
60
サンプルプログラム
61
Chapter 2
振る舞いに関するパターン
2.1 概要
63
64
2.2 Chain of Responsibility パターン
目的
導入理由
65
65
パターンを適用可能な状況
66
65
目次
Chain of Responsibility パターンとは
実装方法
67
長所と短所
68
パターンのバリエーション
関連するパターン
68
69
サンプルプログラム
69
2.3 Command パターン
73
目的
73
導入理由
73
パターンを適用可能な状況
73
Command パターンとは
実装方法
74
74
長所と短所
75
パターンのバリエーション
関連するパターン
76
77
サンプルプログラム
77
2.4 Interpreter パターン
80
目的
80
導入理由
80
パターンを適用可能な状況
80
Interpreter パターンとは
実装方法
80
81
長所と短所
82
パターンのバリエーション
関連するパターン
83
サンプルプログラム
2.5 Iterator パターン
目的
導入理由
82
83
89
89
89
パターンを適用可能な状況
Iterator パターンとは
実装方法
89
90
90
長所と短所
91
パターンのバリエーション
関連するパターン
サンプルプログラム
91
92
92
66
7
8
目次
2.6 Mediator パターン
目的
96
96
導入理由
96
パターンを適用可能な状況
97
Mediator パターンとは
実装方法
97
98
長所と短所
98
パターンのバリエーション
関連するパターン
99
サンプルプログラム
100
2.7 Memento パターン
目的
99
106
106
導入理由
106
パターンを適用可能な状況
106
Memento パターンとは
実装方法
107
107
長所と短所
108
パターンのバリエーション
関連するパターン
109
サンプルプログラム
109
2.8 Observer パターン
目的
109
112
112
導入理由
112
パターンを適用可能な状況
112
Observer パターンとは
実装方法
113
114
長所と短所
115
パターンのバリエーション
関連するパターン
116
サンプルプログラム
2.9 State パターン
目的
導入理由
115
116
122
122
122
パターンを適用可能な状況
State パターンとは
実装方法
長所と短所
123
124
122
122
目次
パターンのバリエーション
関連するパターン
126
サンプルプログラム
2.10 Strategy パターン
目的
125
126
132
132
導入理由
132
パターンを適用可能な状況
132
Strategy パターンとは
長所と短所
133
133
実装方法
133
パターンのバリエーション
関連するパターン
134
サンプルプログラム
2.11 Visitor パターン
目的
134
134
139
139
導入理由
139
パターンを適用可能な状況
Visitor パターンとは
実装方法
140
140
141
長所と短所
142
パターンのバリエーション
関連するパターン
143
143
サンプルプログラム
143
2.12 Template Method パターン
目的
導入理由
149
149
149
パターンを適用可能な状況
149
Template Method パターンとは
実装方法
150
150
長所と短所
151
パターンのバリエーション
関連するパターン
サンプルプログラム
151
151
152
9
10
目次
Chapter 3
構造に関するパターン
3.1 概要
155
156
3.2 Adapter パターン
目的
157
157
導入理由
157
パターンを適用可能な状況
158
Adapter パターンとは
実装方法
158
158
長所と短所
159
パターンのバリエーション
関連するパターン
161
サンプルプログラム
3.3 Bridge パターン
目的
160
161
165
165
導入理由
165
パターンを適用可能な状況
165
Bridge パターンとは
166
継承と Bridge パターンの比較
実装方法
167
長所と短所
167
パターンのバリエーション
関連するパターン
168
168
サンプルプログラム
169
3.4 Composite パターン
目的
172
172
導入理由
172
パターンを適用可能な状況
173
Composite パターンとは
実装方法
173
173
長所と短所
174
パターンのバリエーション
関連するパターン
175
175
サンプルプログラム
176
3.5 Decorator パターン
180
目的
166
180
目次
導入理由
180
パターンを適用可能な状況
181
Decorator パターンとは
実装方法
181
181
長所と短所
182
パターンのバリエーション
関連するパターン
183
サンプルプログラム
3.6 Facade パターン
目的
183
184
189
189
導入理由
189
パターンを適用可能な状況
189
Facade パターンとは
実装方法
189
190
長所と短所
191
パターンのバリエーション
関連するパターン
191
サンプルプログラム
3.7 Flyweight パターン
目的
191
192
197
197
導入理由
197
パターンを適用可能な状況
197
Flyweight パターンとは
実装方法
198
198
長所と短所
199
パターンのバリエーション
関連するパターン
199
199
サンプルプログラム
200
3.8 HOPP(Half-Object Plus Protocol)パターン
目的
導入理由
203
203
パターンを適用可能な状況
HOPP パターンとは
実装方法
203
204
204
長所と短所
205
パターンのバリエーション
関連するパターン
206
206
203
11
12
目次
サンプルプログラム
3.9 Proxy パターン
目的
206
210
210
導入理由
210
パターンを適用可能な状況
Proxy パターンとは
実装方法
210
211
211
長所と短所
212
パターンのバリエーション
関連するパターン
212
212
サンプルプログラム
213
Chapter 4
システムパターン
4.1 概要
217
218
4.2 MVC(Model-View-Controller)パターン
目的
219
導入理由
219
パターンを適用可能な状況
MVC パターンとは
実装方法
220
220
221
長所と短所
222
パターンのバリエーション
関連するパターン
223
サンプルプログラム
4.3 Session パターン
目的
導入理由
223
223
230
230
230
パターンを適用可能な状況
231
Session パターンとは
実装方法
231
232
長所と短所
233
パターンのバリエーション
関連するパターン
233
234
サンプルプログラム
4.4 Worker Thread パターン
234
240
219
目次
目的
240
導入理由
240
パターンを適用可能な状況
240
Worker Thread パターンとは
実装方法
240
241
長所と短所
242
パターンのバリエーション
関連するパターン
243
サンプルプログラム
243
4.5 Callback パターン
目的
243
247
247
導入理由
247
パターンを適用可能な状況
248
Callback パターンとは
実装方法
248
250
長所と短所
251
パターンのバリエーション
関連するパターン
251
252
サンプルプログラム
252
4.6 Successive Update パターン
目的
257
257
導入理由
257
パターンを適用可能な状況
257
Successive Update パターンとは
実装方法
260
長所と短所
260
パターンのバリエーション
関連するパターン
261
サンプルプログラム
4.7 Router パターン
目的
導入理由
261
261
266
266
266
パターンを適用可能な状況
Router パターンとは
実装方法
長所と短所
266
267
267
268
パターンのバリエーション
268
258
13
14
目次
関連するパターン
268
サンプルプログラム
269
4.8 Transaction パターン
目的
272
272
導入理由
272
パターンを適用可能な状況
272
Transaction パターンとは
273
実装方法
274
長所と短所
274
パターンのバリエーション
関連するパターン
275
275
サンプルプログラム
276
Part 2 Java プログラミング言語のパターン
Chapter 5
Java プログラミング言語における
パターンの概要
283
Chapter 6
Java のコア API
6.1 イベント処理
パッケージ
概要
285
286
286
286
パターンの使用方法
289
6.2 JavaBeans
パッケージ
概要
287
289
289
パターンの使用方法
290
6.3 AWT と Swing ― GUI
パッケージ
共通機能
293
293
293
AWT のアーキテクチャモデル
294
目次
Swing のアーキテクチャモデル
パターンの使用方法
Swing におけるパターンの使用方法
6.4 コレクションのフレームワーク
概要
295
296
AWT におけるパターンの使用方法
パッケージ
15
297
298
299
299
299
パターンの使用方法
6.5 入出力(I/O)
303
パッケージ
概要
301
303
303
パターンの使用方法
6.6 リフレクション
パッケージ
概要
304
305
305
305
パターンの使用方法
306
Chapter 7
分散技術
309
7.1 JNDI
310
パッケージ
概要
310
310
パターンの使用方法
7.2 JDBC
313
パッケージ
概要
313
313
パターンの使用方法
7.3 RMI
314
316
パッケージ
概要
316
316
パターンの使用方法
7.4 CORBA
317
319
パッケージ
概要
311
319
319
パターンの使用方法
321
16
目次
Chapter 8
Jini と J2EE のアーキテクチャ
323
324
8.1 Jini
パッケージ
概要
324
324
パターンの使用方法
8.2 J2EE
327
328
概要
328
J2EE の中心概念
328
コンポーネントのパターン
8.3 サーブレットと JSP
パッケージ
概要
331
332
332
332
パターンの使用方法
334
335
8.4 EJB
パッケージ
概要
335
335
一般的なパターンの使用方法
336
コネクタパターンの使用方法(Factory Method パターン)
アーキテクチャパターンの使用方法
337
338
Appendix A
全コード例(PDF ファイル)
339
Appendix B
参考文献
索引
341
345
21
はじめに
なぜパターンを使うのか
「プログラマがコーディングするように大工が家を建てたなら、最初に飛んでくるキツツ
キが文明を滅ぼすだろう」
家を建てるには、どうすればよいのだろうか。
もちろん、樹上に小屋を建てるのをまねることもできる。
1. 頑丈な樹木を見つける。
2. 山ほどの木材、金づち、釘を用意する。
3. 材料を見つけた木に打ち付ける。
4. うまくいくことを願う。
もちろん、この方法を試してみると、期待外れの結果になり、場合によっては樹木だけでな
くその上の小屋も台なしになることがわかる。建築士を探し、助言を得ながら設計図を作るほ
うがより賢い方法だ
しかし、家を建てる専門家である建築士は、どのように意思決定を行うのだろうか。どのよ
うに、何年もの経験から得たことを、新しい家の建築に生かすのだろうか。そこには建築士と
して成功するための何か、つまり、基礎となる知識、経験、場合によるとちょっとした直観力
があるようだ。
家の建築や設計に関する疑問は、ソフトウェア開発の分野で直面する疑問と実はあまり変わ
らない。優れたソフトウェアはどのように設計すればよいのだろうか。柔軟性、拡張性、効率
など、優れた特性を持つソフトウェアを開発するためには、設計においてどのような意思決定
を行えばよいのだろうか。
このような場合、家を建てるときと同様に、経験豊富なアドバイザーが必要になる。建築士
のように、知識と経験をバランスよく備え、ソフトウェア設計における良識のある存在、いわ
ばソフトウェア開発の天才が必要なのだ。
この世にはそのような天才の数は多くない。クローン技術が今よりさらに進歩して天才のコ
ピーが可能にならない限り、自力で何とかしなければならないことが多い。自分たちのプロジェ
クトで、会社で、独自にソフトウェアの専門家を育てていくしかないのである。
そこで、出発点に戻る。優れたソフトウェアを開発したいが、正しい意思決定、つまり最終
的に品質が高い製品を開発できるような意思決定の方法がわからない。経験のあるソフトウェ
アプログラマを育てたいが、脳移植以外には、今の世代のソフトウェアの専門家から効果的な
設計に関する知識を得る方法すら思いつかないのである。
22
はじめに
その「知識を得る方法がある」と考えてみよう。天才の持つノウハウを得ることが、脳移植な
どという馬鹿げた方法を持ち出さずに可能であるとする。ソフトウェア設計の中心概念を記録
してまとめ、次世代のソフトウェア開発者のための基盤を築くことができるのならどうか。
それが可能なのである。この方法は、デザインパターンと呼ばれている。
専門家は、しばしば過去に使った解決法を適用して新しい問題を解決する。目の前の問題を
分析して、過去に起きた問題と似た部分を探す。次に、過去の問題の解決法を思い出し、それ
を法則化する。最後に、導き出された一般的な解決法を現在の問題に適用する。
デザインパターンの考え方は、ソフトウェア開発において、よくある問題の一般的な解決法
に相当する標準手法を作成するというものである。この方法には、次のような長所がある。
●
パターンのカタログを蓄積できる。ソフトウェア開発の初心者は、長年にわたって蓄積さ
れた経験をより効率的に習得できることになる。
●
ソフトウェア設計の意思決定に関するトレードオフ、つまり開発における選択肢のプラス
とマイナスを一定の形式で記録できる。パターンを標準化すると、開発にかかわるすべて
のエンジニアは、初心者および経験者に関係なく、自分の意思決定の意味についてはっき
りと理解しやすくなる。
●
デザインパターンにより、用語が共通化される。その結果、開発中の意思決定に関してコ
ミュニケーションが容易になる。設計を詳細に説明するのではなく、パターン名を使って
意図を示すことができる。
●
エンジニアが相互にパターンを理解できるため、1 つのプロジェクトにおいて共存するパ
ターンを簡単に知ることができる。
デザインパターンにより、オブジェクト指向プログラミングの分野では、経験を共有するた
めの効果的な手段が得られる。開発言語が C++、Smalltalk、Java のいずれであっても、専門
知識の背景が Web プロジェクト、レガシーシステムの統合、特定のシステムのいずれであって
も、経験を集大成し、他のエンジニアと共有できるのだ。長い目で見ると、業界全体でのソフ
トウェア開発の品質の向上に貢献することができる。
パターンの歴史
「そいつは宇宙からやってきた……UCB 経由でね」
ソフトウェア開発におけるデザインパターンという概念は、カリフォルニア大学バークレー
校(UCB)の Christopher Alexander 教授(建築学)のひらめきによるものとされている。1970
年代後半、Alexander はパターンという概念を取り入れ、設計建築のパターンのカタログを作
成した。
この業績はオブジェクト指向分野の興味をかき立て、その後 10 年間で多くの先駆者がソフ
トウェア設計のパターンを生み出した。Kent Beck と Ward Cunningham はその先達となり、
1987 年の OOPSLA というカンファレンスで Smalltalk のデザインパターンのセットを発表し
はじめに
23
た。James Coplien もパターンの「教義」の普及に熱心に努めた 1 人で、C++のイディオム、つ
まり C++による開発のパターンに関する書籍を 1990 年代前半に出版した。
OOPSLA(Object-Oriented Programming, Systems, Languages, and Applications)は、互い
のアイデアを披露できることから、広がりつつあったパターンの概念の賛同者が集まる絶好の
場所となった。「パターン運動」の展開にとって重要となったもう 1 つの場所は、Kent Beck と
Grady Booch によって設立された Hillside Group である。
おそらく、デザインパターンの普及に貢献したとして最もよく知られているのは、1995 年
に出版された Design Patterns: Elements of Reusable Object-Oriented Software*1(Addison
Wesley)である。著者の Erich Gamma、Richard Helm、Ralph Johnson、John Vlissides は、
GoF(Gang of Four、4 人のギャング)としてもよく知られている。この本は包括的なパターン
言語を紹介し、本文で取り上げたパターンに対応する C++言語のサンプルコードを掲載してい
た。また、パターンの概念に勢いをつけることになった別の重要な書籍として、Buschmann、
Meunier、Rohnert、Sommerlad、Stal による共著 Pattern-Oriented Software Architecture, A
System of Patterns*2(John Wiley & Sons、1996 年)がある。
この 2 冊の書籍が刊行されて以来、デザインパターンはソフトウェア開発の分野で相当の関
心を集めてきた。Java 技術は、パターンが幅広い支持を得てきたのと同時に発達してきたため、
Java 開発者は当然のようにデザインパターンをプロジェクトに取り入れることに興味を持った。
Java の開発におけるデザインパターンの人気の急上昇は、JavaOne のようなカンファレンスで
のセッションだけでなく、Java の業界誌に掲載されるパターンに関する記事からもわかる。
パターンの基本概念
「能書きを並べる」
パターンの考え方では、共通の問題とその解決法に関する情報を標準化するという概念が中
心になる。Alexander の最も役立つ成果は、パターンを表現するテンプレートの開発であった。
これは現在ではフォームまたはフォーマットと呼ばれている。Alexander 方式のフォームでは、
5 つのトピック領域を設けてパターンとその解決法の議論を公式化する。
基本的には、1 つのパターンに、そのパターンと「このパターンの働きは何か」を表す名前を
付けることが重要である。さらに、問題の議論、パターンがどのようにその問題を解決するか、
パターンを使う場合の長所、短所、トレードオフなどの説明を含む必要がある。
当然ながら、パターンがオブジェクト指向の分野で取り入れられた頃、ソフトウェア開発に
おけるニーズに対応するため、Alexander 方式のフォームの変形が考えられた。今日使用され
ているフォームのほとんどは、「正統なフォーム」または「GoF フォーム」のいずれかに由来す
*1 編注:邦訳は、本位田真一、吉田和樹監訳『オブジェクト指向における再利用のためのデザインパターン』
(ソフトバンクパブリッシング、1995 年)を参照。1999 年に改訂版が出版されている。
*2 編注:邦訳は、金沢典子、水野貴之、桜井麻里、関富登志、千葉寛之訳『ソフトウェアアーキテクチャ ―
ソフトウェア開発のためのパターン体系』
(近代科学社、2000 年)を参照。
24
はじめに
る。本書は、「GoF フォーム」に基づき、次に示すトピックによりパターンのテンプレートを構
成する。
●
名前 パターンの名前
●
別名 存在する場合のみ
●
パターンの特性 パターンを次の 2 つのトピックによって分類する。
(種類)
・ 生成 オブジェクトを生成する。
・ 振る舞い オブジェクト間の機能の相互動作を調整する。
・ 構造 オブジェクト間の静的かつ構造的な関係を管理する。
・ システム システムレベルでの相互動作を管理する。
(レベル)
・ 単一のクラス 単一のクラスに適用されるパターンを示す。
・ コンポーネント クラスのグループに関連するパターンを示す。
・ アーキテクチャ システムとサブシステムの動作を調整するために使用されるパターン
を示す。
●
目的 パターンが関連する機能を簡単に説明する。
●
導入理由 パターンが役立つと思われる問題について、例を使って簡単に説明する。
●
パターンを適用可能な状況 パターンを使用する状況とその理由を示す。
●
XX パターンとは パターンについて、動作の内容としくみを詳しく説明する。
●
実装方法 パターンを実装するために必要な作業について説明する。パターンを使用する
場合、実装方法についてはこのトピックを参照すること。
●
長所と短所 パターンを使用した結果とパターンを使用する場合のデメリットを示す。
●
パターンのバリエーション 実装方法の代替案とパターンのバリエーションを示す。
●
関連するパターン このパターンに関連する(または密接に関係する)パターンを示す。
●
サンプルプログラム Java コードによるサンプルプログラムを示す。
ソフトウェアの抽象化と再利用
あるいは「もう一度実行してくれよ」
デザインパターンは、ソフトウェアの抽象化と再利用における重要な進化の一歩を意味する。
この 2 つの概念は、プログラミングの考え方では中心となるものであり、「重要な概念そのも
の」とみなす意見もある。
抽象化は、開発者が複雑な問題を徐々に単純な問題に分解することによって問題を解決する
手法である。単純な問題の解決法は、ラベルや名前により「タグを付ける」と、開発者が日々直
面する複雑な問題を解決するための基本単位として使用できる。
はじめに
25
再利用も、抽象化と同様にソフトウェア開発にとって不可欠なものである。ある意味で、ソ
フトウェア開発の歴史は、コードの優れた再利用方法を徐々に探し出すための絶え間ない探求
という性質を持つ。なぜこの話題に関心が集まるのだろう。動機は何だろうか。実際に、ソフ
トウェア開発の性質という観点から見ると、再利用は完全に理解可能なゴールである。たとえ
ば、納期の厳しい複雑なソフトウェアプロジェクトがある場合、次の方法のどちらを選ぶだろ
うか。正しい答えを選択してみよう。
●
すべてのコードをまったく新規に作成する。完成したコードをテストして確認するために、
時間がかかり、苦痛の伴う作業工程に自分と周囲のスタッフを巻き込むことになる。
●
実績があり、テスト済みのコードをベースに作業を進める。
誤解しないでほしいのだが、コーディングは楽しいものである。開発者が一般に好まないの
は、テスト、デバッグ、文書の作成、リリース後のサポートなのだ。長い間、コードや開発の
概念を再利用するために非常に多くの方法が考えられてきた。
●
断片的に再利用する。これは最も初期の再利用方法で、CaP(Cut and Paste、カットアン
ドペースト)とも呼ばれた。これをソフトウェアの効果的な再利用方法であるとはなるべ
く言わないほうがよい。また、この方法では、コードの抽象化という点では何ら品質上の
メリットはもたらされない。
●
アルゴリズムを再利用する。これは再利用を管理するより一般的な方法をもたらした。検
索や並べ替えなどのアルゴリズムを再利用すれば、特定の種類の計算における問題を解決
するための方法(通常は数学的なもの)を抽象化できる。
●
関数を再利用する。または逆にデータ構造を再利用する。これによりコーディングの抽象
化をより直接的に再利用できるようになった。たとえば、アドレスのようなものをモデル
化したい場合、他のプロジェクトでアドレスを必要とした構造を再利用すればよい。同様
に、computeTax のような演算を関数(プログラミング言語に応じてプロシージャ、サブルー
チン、メソッドなど)として定義し、その後全体を新しいプロジェクトとしてコピーする
こともできる。
このような再利用の概念を拡大したものが、関数ライブラリと API である。関数ライブラリ
と API は関数をパッケージとしてまとめ、実際にコードをコピーすることなく将来アプリケー
ションで関数を使用できるようにする方法である。
オブジェクト指向言語の開発は、抽象化と再利用という点では驚異的な進化を意味する。こ
の技術により、コードをさらに活用するためにより高度な次世代の手法が誕生した。
クラスをオブジェクトの設計図としてとらえる概念の登場により、関数とデータの抽象化と
いう従来からある 2 つのメカニズムが組み合わされ、飛躍的に進歩した。エンティティの構造
(データ)とエンティティに適用される関数(振る舞い)をパッケージにまとめることにより、ソ
フトウェアコンポーネントを効果的に再利用する方法が得られるのである。
クラスの中心的な概念を超越して、オブジェクト指向言語により、既存のコードを再利用す
るためのさまざまな方法を選択できるようになる。たとえば、サブクラスとインターフェイス
26
はじめに
の概念により、ソフトウェア開発における再利用に新たな可能性が生まれた。最終的には、ク
ラスのグループが相互に関連し、1 つの論理的なソフトウェアコンポーネントとして効果的に
処理されるようになり、システムレベルでの再利用の非常に強力なモデルとなっている。
次に、ソフトウェアの再利用と抽象化の方法を比較する表を示す。
表 1 再利用と抽象化の方法
再利用の種類
再利用性
抽象化
汎用性
断片的な再利用
非常に低い
なし
非常に低い
データ構造の再利用
高い
データ型
中∼高い
関数の再利用
高い
メソッド
中∼高い
テンプレートの再利用
高い
分類する操作
高い
アルゴリズムの再利用
高い
公式
高い
クラス(インターフェイス、ポリ
モーフィズム、抽象クラス)
高い
データ+メソッド
高い
コードライブラリ
高い
関数
高い∼非常に高い
API
高い
ユーティリティクラス
高い∼非常に高い
コンポーネント
高い
クラスのグループ
高い∼非常に高い
デザインパターン
非常に高い
問題の解決法
非常に高い
「再利用性」は、再利用可能な度合いを表している。
「抽象化」は抽象化されているものを示す。「汎用性」は、コードを再び作成したり、修正す
ることなくこの方法を適用できる度合いを表している。各方法の再利用性は、効果的にその方
法が適用されている程度に応じて大きく異なる。当然、どの方法でも、有効に利用されたり、
誤って使用される可能性がある。
デザインパターンにおける最も興味深い可能性は、他の再利用の技術を開発者としてより効
果的に適用できるようになるということだ。たとえば、1 つのパターンによって特定の状況で
継承を効果的に管理するためのガイドラインや、特定の問題を解決するためのクラスの関係を
効果的に指定するガイドラインが示されることもある。
まとめ
デザインパターンは、ソフトウェア開発において非常に役立つツールである。デザインパター
ンを使用すれば、より効果的なコーディングが可能になる。本書は、数多くのデザインパター
ンのうち、最もよく知られているものの一部を紹介する。さあ、パターンの世界へようこそ。
Part 1
生成に関するパターン
29
振る舞いに関するパターン
63
構造に関するパターン
155
システムパターン
217
Chapter 1
生成に関するパターン
Abstract Factory パターン
Builder パターン
Factory Method パターン
Prototype パターン
Singleton パターン
30
Chapter 1 生成に関するパターン
1.1 概要
生成に関するパターン(Creational Pattern)は、オブジェクト指向プログラミングにおける
最も一般的なタスクの 1 つである、システムにおけるオブジェクトの生成をサポートする。複
雑なオブジェクト指向システムの多くでは、長い間に多数のオブジェクトのインスタンスを生
成する必要がある。生成に関するパターンは、次の機能の提供を支援することにより、生成処
理をサポートする。
●
汎用的なインスタンスの生成 コードの特定のクラスの種類を識別することなく、システ
ム上でオブジェクトの生成を可能にする。
●
簡易性 一部のパターンはオブジェクトの生成処理を簡易化することから、呼び出し側で
はオブジェクトのインスタンスを生成するために複雑なコードを大量に書く必要がない。
●
生成の制約 一部のパターンは、システム上で生成できるオブジェクトの種類や数を強制
的に制約する。
本章では、次のパターンについて説明する。
●
Abstract Factory 具象クラスを指定せずに、関連オブジェクトまたは従属オブジェクト
の集合を生成するための規約を提供する。
●
Builder 別のクラスのインスタンスを構築することを目的としたクラスを定義し、それに
より複雑なオブジェクトの生成を簡易化する。たとえば、ビルダーによって 1 つのメイン
の製品を生成することにより、その製品内で複数のクラスの生成が可能になるが、メイン
のクラスは常に 1 つだけである。
●
Factory Method コンストラクタとは別にオブジェクトを生成するための標準的なメソッ
ドを定義する。ただし、生成するオブジェクトの種類を決めるのはサブクラスになる。
●
Prototype オブジェクトがオブジェクトそのものの複製を生成するクラスを定義するこ
とにより、オブジェクトの動的な生成を容易にする。
●
Singleton このクラスのインスタンスを 1 つだけシステムに持たせ、他のクラスからこの
インスタンスへのアクセスを可能にする。
前述のパターンのうち、Abstract Factory と Factory Method は、明らかに柔軟なオブジェ
クトの生成を定義するという概念に基づいている。この 2 つのパターンでは、生成されるクラ
スまたはインターフェイスが実装システムで拡張されることが前提となる。したがって、他の
生成パターンと組み合わせて使うことが多い。
1.2 Abstract Factory パターン
31
1.2 Abstract Factory パターン
別名 Kit、Toolkit
種類 生成に関するパターン、オブジェクト
レベル コンポーネント
目的
具象クラスを指定せずに、関連オブジェクトまたは従属オブジェクトの集合を生成するため
の規約を提供する。
導入理由
PIM(Personal Information Manager)アプリケーションの一部として、住所と電話番号を管
理する場合を想定してみよう。この PIM アプリケーションは、アドレス帳、スケジュール帳、
予定管理ツールを組み合わせて機能し、住所と電話番号のデータを頻繁に使用することになる。
まず、住所と電話番号のデータを表すクラスを作成できる。これらのクラスは関連情報を格
納し、その形式に関するビジネス規則を適用するようにコードを書く。たとえば、北米の電話
番号はすべて 10 桁に制限し、郵便番号は特定の形式に定める。しかし、クラスを作成した後
に、オランダなどの他の国の住所や電話番号を管理しなければならないことが判明した。
オランダには、有効な電話番号と住所を構成するための別の規則があるため、Address クラ
スと PhoneNumber クラスのロジックを、追加される国の規則を考慮するように変更する。
個人の人脈が広がるにつれて、情報管理の対象となる国が次々と増える。ビジネス規則が増
えるたびに、基本の Address クラスと PhoneNumber クラスはさらに多くのコードで肥大し、管
理が困難になる。しかも、このコードは脆弱であるため、国が追加されるたびに、クラスを変
更し、再コンパイルして連絡先を管理する必要が生じる。
住所と電話番号のようにペアになっているクラスは、システムに柔軟に追加したほうがよい。
つまり、住所と電話番号のデータに適用する一般的な規則を採用し、国の数に関係なくその国
に対応するクラスをシステムに「ロード」できるようにすればよい。
この問題を解決するのが、Abstract Factory
(
「抽象的な工場」
)
パターンである。このパターンを
使用し、Address と PhoneNumber という一般的なパターンに従ったオブジェクトを生成するため
の汎用的なフレームワークとして AddressFactory を定義する。実行時に、AddressFactory ク
ラスはさまざまな国の具象 Factory クラスと組み合わされる。それぞれの国には独自の Address
クラスと PhoneNumber クラスが存在する。
これで、関数のロジックを次々にクラスに追加する必要はなくなる。代わりに、Address か
ら DutchAddress を、PhoneNumber から DutchPhoneNumber を拡張するだけで済む。この 2 つ
のクラスのインスタンスは、DutchAddressFactory によって生成される。結果として、システ
ムの他の部分に大規模な構造の変更を加えることなく、これまでより自由にコードを拡張する
ことができる。
32
Chapter 1 生成に関するパターン
パターンを適用可能な状況
Abstract Factory パターンは、次のような場合に使用する。
●
クライアントが製品を生成する方法に依存しない。
●
アプリケーションが複数の製品の集合で構成される。
●
相互に互換性を保つためにオブジェクトをセットとして生成しなければならない。
●
クラスの集合を提供する場合に、クラスの実装ではなく規約と関係だけを公開したい。
Abstract Factoryパターンとは
アプリケーションは、異なるリソースやオペレーティングシステムを使用しなければならな
い場合がある。たとえば、一般的には次のようなものが考えられる。
●
ウィンドウ機能(アプリケーションの GUI)
●
ファイルシステム
●
他のアプリケーションやシステムとの通信
このような場合、新しいリソースが導入されるたびに再コーディングを行わずにさまざまな
リソースを使用できるように、柔軟なアプリケーションを作成することが求められる。
この問題を解決する有効な方法の 1 つとして、汎用的にリソースを生成する抽象ファクトリ
クラスを定義する方法がある。このクラスは、1 つ以上の生成メソッドを持ち、このメソッド
を呼び出して汎用的なリソースまたは抽象的な製品を生成できる。
Java は多くのプラットフォームで実行されており、それぞれのプラットフォームでは異なる
ファイルシステムやウィンドウ機能の実装が使用される。Java が採用するソリューションでは、
ファイルやウィンドウの概念を抽象化し、具体的な実装を隠す。このため、リソースが実際の
機能を表しているかのように、リソースの汎用的な機能を使用してアプリケーションを開発す
ることができる。
実行時には ConcreteFactory と ConcreteProduct を生成し、アプリケーションで使用する。
具象クラスは AbstractFactory と AbstractProduct によって定義された規約に準拠している
ため、具象クラスも再コーディングや再コンパイルを行わずに直接使用することができる。
実装方法
図 1-1 に、Abstract Factory パターンのクラス図を示す。
Abstract Factory パターンを実装するには、通常、次のクラスを使用する。
●
AbstractFactory AbstractProduct オブジェクトを生成する create メソッドを定義す
る抽象クラスまたはインターフェイスを表す。
●
AbstractProduct アプリケーションが使用するリソースの汎用的な振る舞いを記述する
抽象クラスまたはインターフェイスを表す。
1.2 Abstract Factory パターン
33
interface
AbstracFactory
+ProductA createProductA()
+ProductB createProductB()
ConcreteFactory1
ConcreteFactory2
+ProductA createProductA()
+ProductB createProductB()
+ProductA createProductA()
+ProductB createProductB()
ProductA
ConcreteProductA1
図 1-1
●
ConcreteProductA2
ProductB
ConcreteProductB1
ConcreteProductB2
Abstract Factory パターンのクラス図
ConcreteFactory AbstractFactory の派生クラスで、ConcreteProduct オブジェクトを
生成する 1 つ以上の create メソッドを実装する。
●
ConcreteProduct AbstractProduct の派生クラスで、特定のリソースまたは動作環境の
ための実装を提供する。
長所と短所
Abstract Factory パターンにより、アプリケーション全体の柔軟性が向上する。アプリケー
ションの柔軟性は、設計時および実行時のどちらにおいても明らかである。設計時には、アプ
リケーションの将来の用途をすべて予測する必要がない。代わりに、汎用的なフレームワーク
を作成し、他のアプリケーションから独立して実装を開発する。実行時には、アプリケーショ
ンは新しい機能やリソースを容易に統合することができる。
Abstract Factory パターンにはさらに、アプリケーションの他の部分のテストが容易になる
という長所がある。TestConcreteFactory と TestConcreteProduct の実装は簡単で、予測さ
れるリソースの動作をシミュレートすることができる。
Abstract Factory パターンの長所を理解するには、AbstractProduct の汎用インターフェイ
スを適切に定義する方法を慎重に検討しなければならない。AbstractProduct を適切に定義し
ないと、目的の ConcreteProduct の生成が難しくなったり、不可能になることがある。
34
Chapter 1 生成に関するパターン
パターンのバリエーション
すでに述べたように、AbstractFactory と AbstractProduct は、アプリケーションのニーズ
や好みに応じてインターフェイスまたは抽象クラスとして定義することができる。
AbstractFactory の使用方法に応じて、複数の ConcreteFactory オブジェクトを生成し、同
時に複数の ConcreteProduct の集合を使用するアプリケーションを開発できる。
関連するパターン
Abstract Factory に関連するパターンを次に示す。
●
Factory Method(46ページ) Abstract Factory パターンを実装するために使用する。
●
Singleton(58ページ) ConcreteFactory でよく使用する。
●
*1
Data Access Object
(
[CJ2EEP]
)
Data Access Object パターンでは、Abstract Factory
パターンを使用してデータベースに固有の ConcreteFactory をより柔軟に生成できる。
サンプルプログラム
次のコードは、Abstract Factory パターンを使用した PIM アプリケーションの例である。こ
の例では、さまざまな国の住所や電話番号をサポートする方法を示す。AddressFactory イン
ターフェイスは抽象的なファクトリを表す。
リスト 1-1
1:
2:
3:
4:
AddressFactory.java
public interface AddressFactory{
public Address createAddress();
public PhoneNumber createPhoneNumber();
}
AddressFactory では、createAddress と createPhoneNumber という 2 つの生成メソッドが
定義されることに注意しよう。このメソッドにより、抽象クラスの Address と PhoneNumber が
生成される。Address と PhoneNumber では、それぞれがサポートするメソッドを定義している。
リスト 1-2
1:
2:
3:
4:
5:
Address.java
public abstract class Address{
private String street;
private String city;
private String region;
private String postalCode;
6:
*1 注:[CJ2EEP]は、参考文献の J2EE パターンを意味する(Appendix B を参照)
。
1.2 Abstract Factory パターン
35
public static final String EOL_STRING =
System.getProperty("line.separator");
public static final String SPACE = " ";
7:
8:
9:
10:
public
public
public
public
public
11:
12:
13:
14:
15:
String getStreet(){ return street; }
String getCity(){ return city; }
String getPostalCode(){ return postalCode; }
String getRegion(){ return region; }
abstract String getCountry();
16:
public String getFullAddress(){
return street + EOL_STRING +
city + SPACE + postalCode + EOL_STRING;
}
17:
18:
19:
20:
21:
public void setStreet(String newStreet){ street = newStreet; }
public void setCity(String newCity){ city = newCity; }
public void setRegion(String newRegion){ region = newRegion; }
public void setPostalCode(String newPostalCode)
{ postalCode = newPostalCode; }
22:
23:
24:
25:
26:
27:
}
リスト 1-3
1:
2:
3:
PhoneNumber.java
public abstract class PhoneNumber{
private String phoneNumber;
public abstract String getCountryCode();
4:
public String getPhoneNumber(){ return phoneNumber; }
5:
6:
public void setPhoneNumber(String newNumber){
try{
Long.parseLong(newNumber);
phoneNumber = newNumber;
}
catch (NumberFormatException exc){
}
}
7:
8:
9:
10:
11:
12:
13:
14:
15:
}
Address と PhoneNumber はこの例では抽象クラスとして定義するが、すべての具象クラスで
共通して使用するコードを定義する必要がない場合にはインターフェイスとして簡単に定義し
てもよい。
システムに具体的な機能を提供するには、ConcreteFactory クラスと ConcreteProduct ク
ラスを作成する必要がある。この場合、AddressFactory を実装するクラスを定義し、さらに
Address クラスと PhoneNumber クラスを拡張するサブクラスを定義する。次の 3 つのクラスの
例では、この操作を北米の住所データに対して実行する方法を示している。
36
リスト 1-4
1:
2:
3:
4:
Chapter 1 生成に関するパターン
USAddressFactory.java
public class USAddressFactory implements AddressFactory{
public Address createAddress(){
return new USAddress();
}
5:
public PhoneNumber createPhoneNumber(){
return new USPhoneNumber();
}
6:
7:
8:
9:
}
リスト 1-5
1:
2:
3:
USAddress.java
public class USAddress extends Address{
private static final String COUNTRY = "UNITED STATES";
private static final String COMMA = ",";
4:
public String getCountry(){ return COUNTRY; }
5:
6:
public String getFullAddress(){
return getStreet() + EOL_STRING +
getCity() + COMMA + SPACE + getRegion() +
SPACE + getPostalCode() + EOL_STRING +
COUNTRY + EOL_STRING;
}
7:
8:
9:
10:
11:
12:
13:
}
リスト 1-6
1:
2:
3:
USPhoneNumber.java
public class USPhoneNumber extends PhoneNumber{
private static final String COUNTRY_CODE = "01";
private static final int NUMBER_LENGTH = 10;
4:
public String getCountryCode(){ return COUNTRY_CODE; }
5:
6:
public void setPhoneNumber(String newNumber){
if (newNumber.length() == NUMBER_LENGTH){
super.setPhoneNumber(newNumber);
}
}
7:
8:
9:
10:
11:
12:
}
AddressFactory、Address、PhoneNumber を使う汎用的なフレームワークにより、新しく追
加する国をサポートするように容易にシステムを拡張できる。追加する国に対応する具象的な
ファクトリクラスと製品クラスを新しく定義すればよい。次に、フランスの住所と電話番号を
表すクラスの例を示す。
1.2 Abstract Factory パターン
リスト 1-7
1:
2:
3:
4:
FrenchAddressFactory.java
public class FrenchAddressFactory implements AddressFactory{
public Address createAddress(){
return new FrenchAddress();
}
5:
public PhoneNumber createPhoneNumber(){
return new FrenchPhoneNumber();
}
6:
7:
8:
9:
}
リスト 1-8
1:
2:
FrenchAddress.java
public class FrenchAddress extends Address{
private static final String COUNTRY = "FRANCE";
3:
public String getCountry(){ return COUNTRY; }
4:
5:
public String getFullAddress(){
return getStreet() + EOL_STRING +
getPostalCode() + SPACE + getCity() +
EOL_STRING + COUNTRY + EOL_STRING;
}
6:
7:
8:
9:
10:
11:
}
リスト 1-9
1:
2:
3:
FrenchPhoneNumber.java
public class FrenchPhoneNumber extends PhoneNumber{
private static final String COUNTRY_CODE = "33";
private static final int NUMBER_LENGTH = 9;
4:
public String getCountryCode(){ return COUNTRY_CODE; }
5:
6:
public void setPhoneNumber(String newNumber){
if (newNumber.length() == NUMBER_LENGTH){
super.setPhoneNumber(newNumber);
}
}
7:
8:
9:
10:
11:
12:
}
37
Fly UP