この本について
書名: Design Patterns: Elements of Reusable Object-Oriented Software
著者: Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides(通称 GoF / Gang of Four)
初版: 1994年
オブジェクト指向設計の「古典中の古典」です。いまだに参照され続けているのはなぜか、読んでその理由を理解した気がしました。
読む前の前提認識と実際
「パターンの辞典」だと思っていましたが、むしろ設計の思想書に近い本でした。
第1章と第2章だけで本の価値の大半が詰まっており、残りのカタログ部分(第3〜5章)はリファレンスとして機能する構成になっています。
核心:2つの設計原則
GoF 全体を貫く原則は2つだけです。これを理解するとパターンの意図がほぼ読み解けるようになります。
原則1:実装ではなくインターフェースにプログラムせよ
変数を具象クラスのインスタンスとして宣言するな、抽象クラスが定義するインターフェースにのみコミットせよ——というものです。
// Bad: 具象クラスに依存
MotifScrollBar sb = new MotifScrollBar();
// Good: インターフェースに依存
ScrollBar sb = guiFactory.createScrollBar();
これにより:
- クライアントは具体的な型を知らなくて済みます
- サブシステム間の実装依存が減ります
- 実行時に実装を差し替えられます(= ポリモーフィズムが活きる)
原則2:クラス継承よりオブジェクト合成を優先せよ
継承はコンパイル時に静的に確定し、親クラスの内部を露出させます(「継承はカプセル化を破壊する」)。
合成はインターフェース越しにのみ依存するため、実行時に差し替え可能で結合が弱くなります。
GoF が何度も強調しているのは、継承の使いすぎがシステムをモノリシックにするという点です。
第2章:ケーススタディが最高の教材
WYSIWYGドキュメントエディタ「Lexi」の設計を通じて、8つのパターンがなぜ必要になるかが語られます。
問題が先にあり、パターンが後に来ます。この順序が重要です。
| 設計課題 | 採用パターン |
|---|---|
| 文書の物理構造を統一的に扱いたい | Composite |
| 改行アルゴリズムを差し替えたい | Strategy |
| スクロールバー・ボーダーを動的に追加したい | Decorator |
| ルックアンドフィールを丸ごと切り替えたい | Abstract Factory |
| ウィンドウシステムへの依存を隠蔽したい | Bridge |
| Undo/Redo を実現したい | Command |
| グリフ構造をトラバースしたい | Iterator |
| グリフを変更せず分析ロジックを追加したい | Visitor |
「変化するものをカプセル化せよ」という一言がすべての出発点になっています。
23パターン早見表
生成パターン(Creational)
| パターン | 一言 |
|---|---|
| Abstract Factory | 関連オブジェクト群を族ごと生成するインターフェース |
| Builder | 構築プロセスと表現を分離し、複数の表現を同一手順で生成 |
| Factory Method | インスタンス化するクラスをサブクラスに委譲 |
| Prototype | プロトタイプのコピーで新オブジェクトを生成 |
| Singleton | インスタンスを1つだけに制限しグローバルアクセスを提供 |
構造パターン(Structural)
| パターン | 一言 |
|---|---|
| Adapter | 互換性のないインターフェースを変換して接続 |
| Bridge | 抽象化と実装を分離し、独立して変化可能にする |
| Composite | 部分-全体の木構造を、個別・集合で統一的に扱う |
| Decorator | サブクラス化不要で動的に責務を追加 |
| Facade | サブシステムへの統一インターフェースを提供 |
| Flyweight | 共有により大量の細粒度オブジェクトを効率化 |
| Proxy | 別オブジェクトへのアクセスを代理・制御 |
振る舞いパターン(Behavioral)
| パターン | 一言 |
|---|---|
| Chain of Responsibility | リクエストをオブジェクトの連鎖に沿って委譲 |
| Command | リクエストをオブジェクトとしてカプセル化、Undo対応 |
| Interpreter | 言語の文法を表現し、文を解釈するオブジェクトを定義 |
| Iterator | 内部構造を公開せず集約要素に順次アクセス |
| Mediator | オブジェクト群の相互作用をカプセル化し疎結合化 |
| Memento | カプセル化を破らずオブジェクト状態をスナップショット保存 |
| Observer | 1対多の依存関係で、状態変化を自動通知 |
| State | 内部状態の変化に応じてオブジェクトの振る舞いを変更 |
| Strategy | アルゴリズム群を定義・カプセル化し交換可能にする |
| Template Method | アルゴリズムの骨格を定義し、一部をサブクラスに委譲 |
| Visitor | クラスを変更せず新しい操作を追加できる |
継承 vs 合成:設計判断のフレームワーク
本書は3つの再利用メカニズムを整理しています。
1. クラス継承(ホワイトボックス再利用)
→ コンパイル時確定、親クラス内部が見える、静的
2. オブジェクト合成(ブラックボックス再利用)
→ 実行時確定、インターフェース越し、動的
3. パラメータ化型(Generics/Template)
→ コンパイル時確定、型パラメータで汎化
GoF の立場は、合成を優先し、継承はインターフェース継承(抽象クラスからの派生)に留めるというものです。
パターン分類の2軸
| クラス | オブジェクト | |
|---|---|---|
| 生成 | Factory Method | Abstract Factory, Builder, Prototype, Singleton |
| 構造 | Adapter(クラス版) | Adapter, Bridge, Composite, Decorator, Facade, Flyweight, Proxy |
| 振る舞い | Interpreter, Template Method | Chain of Responsibility, Command, Iterator, Mediator, Memento, Observer, State, Strategy, Visitor |
- クラスパターン:継承ベース、コンパイル時確定
- オブジェクトパターン:合成ベース、実行時変更可能(大多数)
変化への設計:再設計の8つの原因
GoF が「このような設計は将来の変更を困難にする」として列挙している問題と対処パターンです。
- クラス名を明示してオブジェクト生成 → Abstract Factory, Factory Method, Prototype
- 特定操作への依存 → Chain of Responsibility, Command
- ハードウェア/ソフトウェアプラットフォームへの依存 → Abstract Factory, Bridge
- オブジェクトの表現・実装への依存 → Abstract Factory, Bridge, Memento, Proxy
- アルゴリズムへの依存 → Builder, Iterator, Strategy, Template Method, Visitor
- 密結合 → Abstract Factory, Bridge, Chain of Responsibility, Command, Facade, Mediator, Observer
- サブクラス化による機能拡張 → Bridge, Chain of Responsibility, Composite, Decorator, Observer, Strategy
- 変更困難なクラス → Adapter, Decorator, Visitor
フレームワークとパターンの違い
本書の終盤で整理されている重要な対比です。
| デザインパターン | フレームワーク | |
|---|---|---|
| 抽象度 | 高い(概念) | 低い(コードに落とせる) |
| 規模 | アーキテクチャの部品 | アプリケーション全体の骨格 |
| 特化度 | 汎用(どんなドメインにも) | 特定ドメイン(UIフレームワーク等) |
成熟したフレームワークには複数のデザインパターンが含まれています。
パターンを知っていれば、フレームワークの学習コストが下がります。
これが本書の実用的価値の一つだと思います。
読んでの所感
GoF が1994年に書いたものが、30年後の Kotlin/Java コードレビューでもまだ普通に引用されています。それ自体が「設計原則の普遍性」を証明していると感じました。
ただし、現代語に読み替えが必要な箇所もあります。
- Strategy → Kotlin では関数型(高階関数)で代替できるケースが多い
-
Iterator → 言語組み込みの
for/sequenceで抽象化済み - Singleton → DI(依存性注入)でほぼ不要になった
-
Factory Method →
companion object/create()慣習として日常的に使われている
「パターンそのものを実装する」より「パターンの意図を理解する」方が価値が高いと思います。本書の言い方を借りれば、パターンは設計の語彙です。議論を効率化するための共通言語として機能します。
こんな人に勧めます
- 「設計原則は知っているが、具体的な判断軸がほしい」エンジニア
- コードレビューで「なぜこの構造にしたか」を言語化したい方
- Clean Architecture や DDD の設計判断を補強したい方
逆に、パターンの細かい実装コードを求めるなら別の本の方が向いています。本書のコードは C++ / Smalltalk なので、現代言語での実装は各自で読み替える必要があります。
関連書籍
- Clean Architecture (Robert C. Martin) — アーキテクチャレベルへの拡張
- Head First Design Patterns — より入門向け、Java
- Refactoring (Martin Fowler) — パターン適用の実践的コンテキスト