0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【書評】Design Patterns [Elements of Reusable Object-Oriented Software]

0
Posted at

この本について

書名: 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 が「このような設計は将来の変更を困難にする」として列挙している問題と対処パターンです。

  1. クラス名を明示してオブジェクト生成 → Abstract Factory, Factory Method, Prototype
  2. 特定操作への依存 → Chain of Responsibility, Command
  3. ハードウェア/ソフトウェアプラットフォームへの依存 → Abstract Factory, Bridge
  4. オブジェクトの表現・実装への依存 → Abstract Factory, Bridge, Memento, Proxy
  5. アルゴリズムへの依存 → Builder, Iterator, Strategy, Template Method, Visitor
  6. 密結合 → Abstract Factory, Bridge, Chain of Responsibility, Command, Facade, Mediator, Observer
  7. サブクラス化による機能拡張 → Bridge, Chain of Responsibility, Composite, Decorator, Observer, Strategy
  8. 変更困難なクラス → Adapter, Decorator, Visitor

フレームワークとパターンの違い

本書の終盤で整理されている重要な対比です。

デザインパターン フレームワーク
抽象度 高い(概念) 低い(コードに落とせる)
規模 アーキテクチャの部品 アプリケーション全体の骨格
特化度 汎用(どんなドメインにも) 特定ドメイン(UIフレームワーク等)

成熟したフレームワークには複数のデザインパターンが含まれています。
パターンを知っていれば、フレームワークの学習コストが下がります。
これが本書の実用的価値の一つだと思います。


読んでの所感

GoF が1994年に書いたものが、30年後の Kotlin/Java コードレビューでもまだ普通に引用されています。それ自体が「設計原則の普遍性」を証明していると感じました。

ただし、現代語に読み替えが必要な箇所もあります。

  • Strategy → Kotlin では関数型(高階関数)で代替できるケースが多い
  • Iterator → 言語組み込みの for / sequence で抽象化済み
  • Singleton → DI(依存性注入)でほぼ不要になった
  • Factory Methodcompanion object / create() 慣習として日常的に使われている

「パターンそのものを実装する」より「パターンの意図を理解する」方が価値が高いと思います。本書の言い方を借りれば、パターンは設計の語彙です。議論を効率化するための共通言語として機能します。


こんな人に勧めます

  • 「設計原則は知っているが、具体的な判断軸がほしい」エンジニア
  • コードレビューで「なぜこの構造にしたか」を言語化したい方
  • Clean Architecture や DDD の設計判断を補強したい方

逆に、パターンの細かい実装コードを求めるなら別の本の方が向いています。本書のコードは C++ / Smalltalk なので、現代言語での実装は各自で読み替える必要があります。


関連書籍

  • Clean Architecture (Robert C. Martin) — アーキテクチャレベルへの拡張
  • Head First Design Patterns — より入門向け、Java
  • Refactoring (Martin Fowler) — パターン適用の実践的コンテキスト
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?