Comments
Description
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