ビルダーとプロトタイプのパターン: オブジェクトの柔軟な構築
ビルダー e プロトタイプ それらは、次のような問題に対処する創造的なパターンです。 オブジェクト作成の複雑さ。ビルダーは複雑なオブジェクトを段階的に構築します Prototype は、オブジェクトを最初から作成するのではなく、既存のオブジェクトのクローンを作成します。
🎯 何を学ぶか
- 多くのパラメータを持つ複雑なオブジェクト用のパターンビルダー
- 流暢なインターフェイスとメソッドチェーン
- 効率的なクローン作成のためのプロトタイプ パターン
- 深いコピーと浅いコピー
ビルダーパターン
Il ビルダーパターン 複雑なオブジェクトの構築をそれらのオブジェクトから分離します 表現。オブジェクトに多くのオプションのパラメータとコンストラクタがある場合に便利です 複雑になりすぎてしまいます。
// ❌ Costruttori multipli = confusione
class Pizza {{ '{' }}
constructor(
public size: string,
public cheese?: boolean,
public pepperoni?: boolean,
public bacon?: boolean,
public mushrooms?: boolean
) {{ '{' }}{{ '}' }}
{{ '}' }}
// Difficile ricordare l'ordine dei parametri
const pizza1 = new Pizza("large", true, false, true, false);
const pizza2 = new Pizza("medium", true, true); // Cosa manca?
class Pizza {{ '{' }}
constructor(
public size: string,
public cheese: boolean = false,
public pepperoni: boolean = false,
public bacon: boolean = false,
public mushrooms: boolean = false
) {{ '{' }}{{ '}' }}
{{ '}' }}
class PizzaBuilder {{ '{' }}
private size: string = "medium";
private cheese: boolean = false;
private pepperoni: boolean = false;
private bacon: boolean = false;
private mushrooms: boolean = false;
setSize(size: string): PizzaBuilder {{ '{' }}
this.size = size;
return this; // Method chaining!
{{ '}' }}
addCheese(): PizzaBuilder {{ '{' }}
this.cheese = true;
return this;
{{ '}' }}
addPepperoni(): PizzaBuilder {{ '{' }}
this.pepperoni = true;
return this;
{{ '}' }}
addBacon(): PizzaBuilder {{ '{' }}
this.bacon = true;
return this;
{{ '}' }}
addMushrooms(): PizzaBuilder {{ '{' }}
this.mushrooms = true;
return this;
{{ '}' }}
build(): Pizza {{ '{' }}
return new Pizza(
this.size,
this.cheese,
this.pepperoni,
this.bacon,
this.mushrooms
);
{{ '}' }}
{{ '}' }}
// Utilizzo: chiaro e leggibile!
const pizza = new PizzaBuilder()
.setSize("large")
.addCheese()
.addPepperoni()
.addMushrooms()
.build();
console.log(pizza);
// Pizza {{ '{' }} size: 'large', cheese: true, pepperoni: true, bacon: false, mushrooms: true {{ '}' }}
検証付きビルダー
ビルダーはオブジェクトを作成する前に状態を検証できます。
class User {{ '{' }}
constructor(
public username: string,
public email: string,
public age?: number,
public address?: string
) {{ '{' }}{{ '}' }}
{{ '}' }}
class UserBuilder {{ '{' }}
private username?: string;
private email?: string;
private age?: number;
private address?: string;
setUsername(username: string): UserBuilder {{ '{' }}
this.username = username;
return this;
{{ '}' }}
setEmail(email: string): UserBuilder {{ '{' }}
this.email = email;
return this;
{{ '}' }}
setAge(age: number): UserBuilder {{ '{' }}
this.age = age;
return this;
{{ '}' }}
setAddress(address: string): UserBuilder {{ '{' }}
this.address = address;
return this;
{{ '}' }}
build(): User {{ '{' }}
// Validazione prima della creazione
if (!this.username || this.username.length < 3) {{ '{' }}
throw new Error("Username deve essere almeno 3 caratteri");
{{ '}' }}
if (!this.email || !this.email.includes('@')) {{ '{' }}
throw new Error("Email non valida");
{{ '}' }}
if (this.age && this.age < 0) {{ '{' }}
throw new Error("Età non può essere negativa");
{{ '}' }}
return new User(this.username, this.email, this.age, this.address);
{{ '}' }}
{{ '}' }}
// Utilizzo
try {{ '{' }}
const user = new UserBuilder()
.setUsername("alice")
.setEmail("alice@example.com")
.setAge(25)
.build();
console.log(user); // User creato con successo
{{ '}' }} catch (error) {{ '{' }}
console.error(error.message);
{{ '}' }}
// Errore di validazione
const invalidUser = new UserBuilder()
.setUsername("ab") // Troppo corto!
.setEmail("invalid-email")
.build(); // ❌ Throw Error: "Username deve essere almeno 3 caratteri"
流暢なインターフェース
Builder パターンでは以下を使用します。 流暢なインターフェース (メソッドチェーン) 読み取り可能な API の場合:
class QueryBuilder {{ '{' }}
private table: string = '';
private fields: string[] = ['*'];
private whereConditions: string[] = [];
private orderByField?: string;
private limitValue?: number;
from(table: string): QueryBuilder {{ '{' }}
this.table = table;
return this;
{{ '}' }}
select(...fields: string[]): QueryBuilder {{ '{' }}
this.fields = fields;
return this;
{{ '}' }}
where(condition: string): QueryBuilder {{ '{' }}
this.whereConditions.push(condition);
return this;
{{ '}' }}
orderBy(field: string): QueryBuilder {{ '{' }}
this.orderByField = field;
return this;
{{ '}' }}
limit(value: number): QueryBuilder {{ '{' }}
this.limitValue = value;
return this;
{{ '}' }}
build(): string {{ '{' }}
let query = `SELECT ${{ '{' }}this.fields.join(', '){{ '}' }} FROM ${{ '{' }}this.table{{ '}' }}`;
if (this.whereConditions.length > 0) {{ '{' }}
query += ` WHERE ${{ '{' }}this.whereConditions.join(' AND '){{ '}' }}`;
{{ '}' }}
if (this.orderByField) {{ '{' }}
query += ` ORDER BY ${{ '{' }}this.orderByField{{ '}' }}`;
{{ '}' }}
if (this.limitValue) {{ '{' }}
query += ` LIMIT ${{ '{' }}this.limitValue{{ '}' }}`;
{{ '}' }}
return query;
{{ '}' }}
{{ '}' }}
// Utilizzo: leggibile come linguaggio naturale
const query = new QueryBuilder()
.select('id', 'name', 'email')
.from('users')
.where('age > 18')
.where('active = true')
.orderBy('name')
.limit(10)
.build();
console.log(query);
// "SELECT id, name, email FROM users WHERE age > 18 AND active = true ORDER BY name LIMIT 10"
試作パターン
Il 試作パターン 代わりに既存のインスタンスのクローンを作成して新しいオブジェクトを作成します
使用する new。作成に費用がかかる場合や複雑な場合に役立ちます。
interface Prototype {{ '{' }}
clone(): this;
{{ '}' }}
class Shape implements Prototype {{ '{' }}
constructor(
public x: number,
public y: number,
public color: string
) {{ '{' }}{{ '}' }}
clone(): this {{ '{' }}
// Shallow copy
return Object.create(this);
{{ '}' }}
{{ '}' }}
class Circle extends Shape {{ '{' }}
constructor(
x: number,
y: number,
color: string,
public radius: number
) {{ '{' }}
super(x, y, color);
{{ '}' }}
clone(): this {{ '{' }}
return new Circle(this.x, this.y, this.color, this.radius) as this;
{{ '}' }}
{{ '}' }}
// Utilizzo
const circle1 = new Circle(10, 20, "red", 5);
const circle2 = circle1.clone();
circle2.x = 30;
circle2.color = "blue";
console.log(circle1); // Circle {{ '{' }} x: 10, y: 20, color: 'red', radius: 5 {{ '}' }}
console.log(circle2); // Circle {{ '{' }} x: 30, y: 20, color: 'blue', radius: 5 {{ '}' }}
ディープコピーとシャローコピー
の違い 浅いコピー (浅いコピー) e ディープコピー (ディープコピー) は重要です:
class Address {{ '{' }}
constructor(
public street: string,
public city: string
) {{ '{' }}{{ '}' }}
{{ '}' }}
class Person {{ '{' }}
constructor(
public name: string,
public address: Address
) {{ '{' }}{{ '}' }}
// Shallow copy: copia solo i riferimenti
shallowClone(): Person {{ '{' }}
return new Person(this.name, this.address);
{{ '}' }}
// Deep copy: copia ricorsivamente tutti gli oggetti
deepClone(): Person {{ '{' }}
const addressCopy = new Address(this.address.street, this.address.city);
return new Person(this.name, addressCopy);
{{ '}' }}
{{ '}' }}
const person1 = new Person("Alice", new Address("Via Roma", "Milano"));
// Shallow copy
const person2 = person1.shallowClone();
person2.name = "Bob";
person2.address.city = "Torino"; // ⚠️ Modifica ANCHE person1!
console.log(person1.address.city); // "Torino" ← CONDIVISO!
console.log(person2.address.city); // "Torino"
// Deep copy
const person3 = person1.deepClone();
person3.address.city = "Roma"; // ✅ Non tocca person1
console.log(person1.address.city); // "Torino"
console.log(person3.address.city); // "Roma" ← INDIPENDENTE
レジストリを使用したプロトタイプ
Un プロトタイプレジストリ クローン作成可能なプロトタイプのカタログを維持します。
abstract class Enemy {{ '{' }}
constructor(
public health: number,
public damage: number,
public speed: number
) {{ '{' }}{{ '}' }}
abstract clone(): Enemy;
abstract attack(): void;
{{ '}' }}
class Zombie extends Enemy {{ '{' }}
clone(): Zombie {{ '{' }}
return new Zombie(this.health, this.damage, this.speed);
{{ '}' }}
attack(): void {{ '{' }}
console.log("Zombie attacca!");
{{ '}' }}
{{ '}' }}
class Skeleton extends Enemy {{ '{' }}
clone(): Skeleton {{ '{' }}
return new Skeleton(this.health, this.damage, this.speed);
{{ '}' }}
attack(): void {{ '{' }}
console.log("Skeleton lancia freccia!");
{{ '}' }}
{{ '}' }}
// Registry dei prototipi
class EnemyRegistry {{ '{' }}
private prototypes: Map<string, Enemy> = new Map();
register(type: string, prototype: Enemy): void {{ '{' }}
this.prototypes.set(type, prototype);
{{ '}' }}
create(type: string): Enemy {{ '{' }}
const prototype = this.prototypes.get(type);
if (!prototype) {{ '{' }}
throw new Error(`Prototype ${{ '{' }}type{{ '}' }} non trovato`);
{{ '}' }}
return prototype.clone();
{{ '}' }}
{{ '}' }}
// Setup registry
const registry = new EnemyRegistry();
registry.register("zombie", new Zombie(100, 10, 2));
registry.register("skeleton", new Skeleton(50, 15, 5));
// Creazione rapida di nemici
const enemy1 = registry.create("zombie");
const enemy2 = registry.create("zombie");
const enemy3 = registry.create("skeleton");
enemy1.attack(); // "Zombie attacca!"
enemy3.attack(); // "Skeleton lancia freccia!"
各パターンをいつ使用するか
✅ 次の場合にビルダーを使用します。
- オブジェクトには、 多くのオプションパラメータ
- 作成前に複雑な検証が必要
- 段階的な構成により可読性が向上
- スムーズな API とメソッド チェーンが必要な場合
✅ 次の場合にプロトタイプを使用します。
- オブジェクトを作成することは、 高い (DBクエリ、解析など)
- バリエーションを最小限に抑えた同様のインスタンスを多数必要とする
- 各構成のサブクラスを避けたい
- クローン作成は構築よりも効率的です
実践例: ドキュメントエディタ
// Prototype per documenti
class Document {{ '{' }}
constructor(
public title: string,
public content: string,
public metadata: {{ '{' }} author: string; date: Date {{ '}' }}
) {{ '{' }}{{ '}' }}
clone(): Document {{ '{' }}
return new Document(
this.title,
this.content,
{{ '{' }} ...this.metadata, date: new Date(this.metadata.date) {{ '}' }} // Deep copy
);
{{ '}' }}
{{ '}' }}
// Builder per configurazione complessa
class DocumentBuilder {{ '{' }}
private title: string = "Untitled";
private content: string = "";
private author: string = "Anonymous";
private date: Date = new Date();
setTitle(title: string): DocumentBuilder {{ '{' }}
this.title = title;
return this;
{{ '}' }}
setContent(content: string): DocumentBuilder {{ '{' }}
this.content = content;
return this;
{{ '}' }}
setAuthor(author: string): DocumentBuilder {{ '{' }}
this.author = author;
return this;
{{ '}' }}
build(): Document {{ '{' }}
return new Document(this.title, this.content, {{ '{' }}
author: this.author,
date: this.date
{{ '}' }});
{{ '}' }}
{{ '}' }}
// Utilizzo combinato
const template = new DocumentBuilder()
.setAuthor("System")
.setContent("Template content...")
.build();
// Clona template per nuovi documenti
const doc1 = template.clone();
doc1.title = "Report Q1";
const doc2 = template.clone();
doc2.title = "Report Q2";
結論
ビルダーとプロトタイプを使用すると、複雑なオブジェクトを簡単に作成できます。 ビルダー 読みやすいステップバイステップの構築のために流暢なインターフェイスを使用します。 プロトタイプ 効率を高めるために既存のインスタンスのクローンを作成します。これらはよく一緒に使用されます: 構成用ビルダー 初期、バリアントを迅速に作成するためのプロトタイプ。
🎯 重要なポイント
- ビルダー = メソッドチェーンによる柔軟な構築
- 流暢なインターフェイスによりコードが読みやすくなります
- プロトタイプ = 新しいのではなくクローンを作成する
- 深いコピーと浅いコピー: ネストされたオブジェクトに注意してください
- レジストリは再利用可能なプロトタイプのカタログを維持します







