
FactoryもBuilderもGoFのデザインパターンですが、これらの違いを理解して、活用できるようにしておきましょう。
「Factory(ファクトリ)」と「Builder(ビルダー)」はどちらも“オブジェクト生成の責務を整理するためのパターン”です。
GoFで学習したこの2つのパターンは、目的と適用範囲が異なるので、比較して学習してみましょう。
でも、会いたいするパターンというわけではなく、組み合わせて使うとより効率的になるので、そのサンプルコードも公開しておきますね。
目的の違い(ざっくり比較)
| パターン |
主目的 |
適している場面 |
キー概念 |
| Factory(工場) |
どのクラスを作るかを隠す |
サブクラスや実装を切り替えたい時 |
インスタンス生成の委譲 |
| Builder(建築家) |
構築手順を柔軟に制御する |
複雑なオブジェクトを段階的に作る時 |
構築の分離(Director + Builder) |
Factory(ファクトリ)パターン
概要
オブジェクト生成を一箇所に集約し、newの乱立を防ぐパターンです。
呼び出し側が「どのクラスを作るか」意識しなくて済むと言う特性があります。
Factory Method や Abstract Factory は派生形なので、合わせて覚えておくと便利です。
サンプルコード
class Dog {
speak() { return "ワン!"; }
}
class Cat {
speak() { return "ニャー!"; }
}
class AnimalFactory {
static create(type) {
switch (type) {
case "dog": return new Dog();
case "cat": return new Cat();
default: throw new Error("未知の動物");
}
}
}
// 呼び出し側
const pet = AnimalFactory.create("dog");
console.log(pet.speak()); // ワン!
ポイント
new の具体クラスが隠蔽されるので、それぞれ独自のクラスを作り込むことができます。
実装を差し替えても呼び出し側のコードが変わらないので、依存性が強くなりすぎないので、分割した開発が可能になります。
新しい動物が追加されても、createクラスに追加をすればいいだけです。
Builder(ビルダー)パターン
複雑なオブジェクト生成手順を分離・カプセル化するパターンです。
「構成要素を順番にセット」して最終的に build() で完成させるところが特徴です。
Factoryが「どれを作るか」なら、Builderは「どう作るか」を考えると区分けがしやすいはずですね。
ユーザー登録のサンプルコード
class User {
constructor(name, age, email) {
this.name = name;
this.age = age;
this.email = email;
}
}
class UserBuilder {
setName(name) {
this.name = name;
return this;
}
setAge(age) {
this.age = age;
return this;
}
setEmail(email) {
this.email = email;
return this;
}
build() {
return new User(this.name, this.age, this.email);
}
}
// 使い方
const user = new UserBuilder()
.setName("Alice")
.setAge(25)
.setEmail("alice@example.com")
.build();
console.log(user);
ポイント
構築手順を柔軟にカスタマイズできるようになります。
途中でパラメータが変わっても再構築しやすいのがポイント。
複雑な設定オブジェクトやネスト構造を持つときに有効ですね。
組み合わせパターン
FactoryとBuilderは、組み合わせて使うことで、より効率的な構造を作り出すことができます。
main.js
// === 利用例 ===
const alice = MemberFactory.createFree('Alice', 'alice@example.com');
const bob = MemberFactory.createPremium('Bob', 'bob@example.com');
console.log(alice.info());
console.log(bob.info());
member_factory.js
// === Factory ===
class MemberFactory {
static createFree(name, email) {
return new MemberBuilder().setName(name).setEmail(email).setPlan('free').build();
}
static createPremium(name, email) {
return new MemberBuilder().setName(name).setEmail(email).setPlan('premium').build();
}
}
member_builder.js
// === Builder ===
class MemberBuilder {
constructor() {
this.name = '';
this.email = '';
this.plan = 'free';
this.createdAt = new Date().toISOString();
}
setName(name) {
this.name = name;
return this;
}
setEmail(email) {
this.email = email;
return this;
}
setPlan(plan) {
this.plan = plan;
return this;
}
build() {
return new Member(this.name, this.email, this.plan, this.createdAt);
}
}
member.js
// === Entity ===
class Member {
constructor(name, email, plan, createdAt) {
Object.assign(this, { name, email, plan, createdAt });
}
info() {
return `${this.name} (${this.email}) [${this.plan}] - ${this.createdAt}`;
}
}
ポイント
MemberFactoryのなかで、MemberBuilderを呼び出して、改装的にパターン実装しています。
サンプルコードでは、それぞれをファイル分けることで、それぞれの特徴を活かすようにしています。
ファイル単位で、役割を分割できる上、それぞれのルールをちゃんと設計しておけば、機能追加や修正作業などでトラブルを最小限に抑えることができるでしょう。
あとがき
Director は「どの手順で作るか」を定義する役割で、
Builder は「実際の構築処理」を担当するのが理解できましたか?
両方とも「newの乱立を防ぎ、責務を整理する」ための構造と言う点では、プログラムを効率的に管理しやすくできます。
かなり基本的なパターンなので、プログラミングをする際に意識して設計できるようになると、安定したシステム構築ができるようになるでしょう。
0 件のコメント:
コメントを投稿