Head First デザインパターン
1章 Strategy パターン
一連のアルゴリズムを定義してカプセル化し、交換できるようにする。Strategy パターンを使うと、アルゴリズムを利用するクライアントとは独立してアルゴリズムを変更できる。
設計原則
- アプリケーション内の変化する部分を特定し、不変な部分と分離する。変化する部分は「
カプセル化
」する。すると、その部分はコードの他の部分に影響を及ぼさない。その結果、コード変更による予期せぬ結果が少なくなり、システムの柔軟性が向上する。 - 実装に対してではなく、インターフェースに対してプログラミングする。Duckの振る舞いは別のクラス(特定の振る舞いインタフェースを実装したクラス)に配置する。
- 継承よりコンポジションの方が好ましい。
- 「HAS-A(〜を持つ)」は「IS-A(〜である)」より優れている場合がある
- FlyBehavior と QuackBehavior のように2つのクラスを統合する際は、コンポジション(composition)を使用する。
- 「HAS-A(〜を持つ)」は「IS-A(〜である)」より優れている場合がある
サンプルコード
2章 Observer パターン
オブジェクト間の 1対多 の依存関係を定義し、あるオブジェクトの状態が変化すると、そのオブジェクトに依存しているすべてのオブジェクトに自動的に通知され更新されるようにする。Observer パターンでは、発行者(パブリッシャ)は「サブジェクト」と呼ばれ、購読者(サブスクライバ)は「オブザーバー」と呼ばれる。
設計原則
- 相互にやりとりを行うオブジェクトの間には、疎結合設計を使う。
- 疎結合設計はオブジェクト間の相互依存を最小限にするため、変更に対応できる柔軟なOOシステムを構築できる。
サンプルコード
3章 Decorator パターン
オブジェクトに追加の責務を動的に付与する。デコレーターは、サブクラス化の代替となる、柔軟な機能拡張手段を備える。
設計原則
- クラスは拡張に対しては開かれた状態にするべきだが、変更に対しては閉じた状態にするべき。(開放/閉鎖原則またはオープンクローズドの原則)
サンプルコード
4章 Factory パターン
設計原則
抽象に依存する。具象クラスに依存してはいけない。(依存関係反転の原則または依存関係逆転の原則)
依存関係反転の原則はこちらの説明が分かりやすい。
Factory Method パターン
Factory Method パターンはオブジェクトを作成するためのインタフェースを定義するが、どのクラスをインスタンス化するかについてはサブクラスに決定させる。Factory Method により、クラスはサブクラスにインスタンス化を委ねることができる。
サンプルコード
Abstract Factory パターン
Abstract Factory パターンは、具象クラスを指定せずに、一連の関連オブジェクトや依存オブジェクトを作成するためのインターフェースを提供する。
サンプルコード
5章 Singleton パターン
クラスがインスタンスを1つしか持たないことを保証し、そのインスタンスにアクセスするグローバルポイントを提供する。
使用例
- 共有リソースへのアクセス: データベース接続や設定ファイルへのアクセスなど、アプリケーション全体で共有するリソースにアクセスするとき。
- ログ記録: アプリケーション全体で一つのログファイルに書き込むときなど、同じロガーオブジェクトを使用する場合。
- 状態管理: アプリケーションのライフサイクル全体で持続するような状態を持つオブジェクトの場合。
典型的な Singleton パターンの実装例
// NOTE: この実装はスレッドセーフではありません!
public class Singleton {
private static Singleton uniqueInstance;
private Singleton() {} // コンストラクタを private にすることで、外部からのインスタンス化を防ぐ
// 唯一のインスタンスを返すメソッド
public static Singleton getInstance() {
if (uniqueInstance == null) {
uniqueInstance = new Singleton();
}
return uniqueInstance;
}
}
マルチスレッド、遅延インスタンス作成、二重チェックロッキングを考慮した実装例
マルチスレッドを実現するために単純に synchronized (同期化)した場合、実行の度に同期化されるためオーバーヘッドが大きい。初回以降はインスタンス作成をしないように実装する。また、並列処理で複数インスタンスが作成されないように二重チェックロッキングを使う。
public class Singleton {
private volatile static Singleton uniqueInstance; // volatile は変数をマルチスレッド処理するための修飾子
private Singleton() {}
public static Singleton getInstance() {
if (uniqueInstance == null) { // インスタンスの有無を調べる
synchronized (Singleton.class) { // 同期ブロック
if (uniqueInstance == null) { // 初回実行時のみインスタンス化
uniqueInstance = new Singleton();
}
}
}
return uniqueInstance;
}
}
サンプルコード
6章 Command パターン
リクエストをオブジェクトとしてカプセル化し、その結果、クライアントをさまざまなリクエスト、キュー、またはログリクエストでパラメータ化し、アンドゥ可能な操作もサポートする。
リクエストを行うオブジェクトとそのリクエストの実行方法を知っているオブジェクトを分離したい場合に Command パターンを使う。
RemoteLoader実装例
public class RemoteLoader {
public static void main(String[] args) {
RemoteControlWithUndo remoteControl = new RemoteControlWithUndo();
Light livingRoomLight = new Light("Living Room");
LightOnCommand livingRoomLightOn =
new LightOnCommand(livingRoomLight);
LightOffCommand livingRoomLightOff =
new LightOffCommand(livingRoomLight);
remoteControl.setCommand(0, livingRoomLightOn, livingRoomLightOff);
remoteControl.onButtonWasPushed(0);
remoteControl.offButtonWasPushed(0);
System.out.println(remoteControl);
remoteControl.undoButtonWasPushed();
remoteControl.offButtonWasPushed(0);
remoteControl.onButtonWasPushed(0);
System.out.println(remoteControl);
remoteControl.undoButtonWasPushed();
}
}
サンプルコード
7章 Adapter パターンと Facade パターン
設計原則
最小知識の原則(Principle of Least Knowledge): オブジェクト間のやり取りを少数の身近な「友達」だけに減らすようにするという指針である。
Adapter パターン
クラスのインタフェースをクライアントが要求する別のインタフェースに変換する。アダプタは、インターフェースの互換性がないためにそのままでは連携できないクラスを連携させる。
サンプルコード
Facade パターン
サブシステムの一連のインタフェースに対する、統合されたインタフェースを提供する。ファサードは、サブシステムを使いやすくする高水準インタフェースを定義する。
サンプルコード
8章 Template Method パターン
メソッド内でアルゴリズムの骨組みを定義し、一部の手順をサブクラスに委ねる。Template Method はアルゴリズムの構造を変えることなく、アルゴリズムのある手順をサブクラスに再定義させる。
ハリウッドの原則
こちらを呼び出すな。こちらから呼び出す。(Don't call us, we'll call you.)スーパークラスが主導権を握り、ハリウッドにおける習慣のようにサブクラスを必要とするときにスーパークラスがサブクラスを呼び出す。
具象メソッド customerWantsCondiments() が true を返すことを調べる条件式を追加。客がコンディメンとを「希望する」場合のみ、 addCondiments() を呼び出す。
public abstract class CaffeineBeverageWithHook {
final void prepareRecipe() {
boilWater();
brew();
pourInCup();
if (customerWantsCondiments()) {
addCondiments();
}
}
// 省略
サンプルコード
9章 Iterator パターンと Composite パターン
設計原則
クラスは変更される理由を一つだけ持つべきである。(単一責務の原則)
クラスの責務は、変更される可能性のある部分に及ぶ。複数の責務を持つことは、変更される可能性のある部分が複数あることを意味する。
Iterator パターン
内部表現を公開せずに、アグリゲートオブジェクトの要素に順次アクセスする方法を提供する。
サンプルコード
Composite パターン
オブジェクトをツリー構造に構成して部分−全体階層を表現できる。Composite パターンにより、クライアントは個々のオブジェクトとオブジェクトのコンポジションを統一的に扱える。
サンプルコード