GoF(Gang of Four)デザインパターンは、ソフトウェア設計における一連の再利用可能なソリューションです。GoFデザインパターンは、エリック・ガンマ、リチャード・ヘルム、ラルフ・ジョンソン、ジョン・ブリシディスによって定義され、彼らの著書『デザインパターン要素再利用のためのソフトウェアのオブジェクト指向分析と設計』で紹介されました。以下に、GoFデザインパターンの23種類を示します。
-
デザインパターン: クリエーションパターン
- 抽象ファクトリ(Abstract Factory)
- ビルダ(Builder)
- ファクトリメソッド(Factory Method)
- プロトタイプ(Prototype)
- シングルトン(Singleton)
-
デザインパターン: 構造パターン
- アダプタ(Adapter)
- ブリッジ(Bridge)
- コンポジット(Composite)
- デコレータ(Decorator)
- ファサード(Facade)
- フライウェイト(Flyweight)
- プロキシ(Proxy)
-
デザインパターン: 振る舞いパターン
- チェインオブレスポンシビリティ(Chain of Responsibility)
- コマンド(Command)
- イテレータ(Iterator)
- メディエータ(Mediator)
- メメント(Memento)
- オブザーバ(Observer)
- ステート(State)
- ストラテジ(Strategy)
- テンプレートメソッド(Template Method)
- ビジター(Visitor)
これらのパターンは、異なるソフトウェア設計の問題を解決するためのガイドラインとして使用することができます。各パターンは、特定の問題領域に対して再利用可能なソリューションを提供し、コードの柔軟性、再利用性、保守性を向上させるのに役立ちます。ただし、すべての場合にこれらのパターンが最適であるわけではありません。問題の特性に合わせて適切なパターンを選択する必要があります。
ビルダーパターンとは?
ビルダーパターンは、いわゆる"telescoping constructor anti-pattern"(複数のコンストラクタが多数重なる設計上の問題点)に対する解決策として使われます。このパターンを利用すると、多くのパラメータを持つオブジェクトを段階的に構築することができます。
ビルダーパターンのメリット
- 読みやすさ: 多くのパラメータがあっても、それぞれの意味を明確にできます。
- 柔軟性: 必要なパラメータを設定し、それ以外はデフォルト値を活用できます。
- 不変性: オブジェクトが一度ビルドされると、変更不可能な状態を保つことができます。
Javaでのビルダーパターンの実装例
ビルダパターンを実装する前の典型的なオブジェクト作成方法は、複数のコンストラクタをオーバーロードすることでした。以下は、ビルダパターンを導入する前のUser
クラスの例です。
public class User {
private final String firstName; // 必須
private final String lastName; // 必須
private int age; // オプショナル
private String phone; // オプショナル
private String address; // オプショナル
public User(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
public User(String firstName, String lastName, int age) {
this(firstName, lastName);
this.age = age;
}
public User(String firstName, String lastName, int age, String phone) {
this(firstName, lastName, age);
this.phone = phone;
}
public User(String firstName, String lastName, int age, String phone, String address) {
this(firstName, lastName, age, phone);
this.address = address;
}
// ゲッターとセッターは省略
}
このコードでは、User
オブジェクトを作成するために、異なるパラメータを持ついくつかのコンストラクタが用意されています。これは、クライアントが異なる情報を持つユーザーを作成したい場合に便利ですが、いくつかの問題があります:
- コードの重複: 同じパラメータを取るコンストラクタが増えるごとに、多くのコードの重複が発生します。
- パラメータの増加: 新しいパラメータが必要になるたびに、新しいコンストラクタを追加する必要があります。
- 可読性の問題: コンストラクタのパラメータが多くなると、どのパラメータが何を表しているのかを理解するのが難しくなります。
ビルダパターンを使用すると、これらの問題をエレガントに解決できるので、コードの可読性、保守性、拡張性が向上します。
Javaでビルダーパターンを実装する方法を見てみましょう。以下は、シンプルなユーザークラスにおけるビルダーパターンの一例です。
public class User {
// 必須パラメータ
private final String firstName;
private final String lastName;
// オプショナルパラメータ
private int age;
private String phone;
private String address;
private User(UserBuilder builder) {
this.firstName = builder.firstName;
this.lastName = builder.lastName;
this.age = builder.age;
this.phone = builder.phone;
this.address = builder.address;
}
// Builderクラス
public static class UserBuilder {
// 必須パラメータ
private final String firstName;
private final String lastName;
// オプショナルパラメータ
private int age;
private String phone;
private String address;
public UserBuilder(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
public UserBuilder age(int value) {
this.age = value;
return this;
}
public UserBuilder phone(String value) {
this.phone = value;
return this;
}
public UserBuilder address(String value) {
this.address = value;
return this;
}
public User build() {
return new User(this);
}
}
// Userインスタンスの生成例
public static void main(String[] args) {
User user = new User.UserBuilder("Taro", "Yamada")
.age(30)
.phone("090-1234-5678")
.address("東京都新宿区")
.build();
}
}
この例では、User
オブジェクトの生成に UserBuilder
クラスを利用しています。これにより、必須のパラメータに加えて任意のパラメータも設定し、オブジェクトを構築することが可能になります。
ビルダーパターンは、パラメータが多数必要なオブジェクトの生成に特に有効で、コードの可読性と保守性を大幅に向上させることができます。