CREATIONAL

Builder

Separare la costruzione di un oggetto complesso dalla sua rappresentazione, permettendo allo stesso processo di costruzione di creare diverse rappresentazioni.

Il Problema

Creare oggetti complessi con molti parametri opzionali può portare a costruttori telescopici (costruttori con troppi parametri) o a oggetti parzialmente inizializzati.

La Soluzione

Estrarre il codice di costruzione dell'oggetto in un oggetto separato chiamato builder. Organizzare la costruzione in passi che possono essere chiamati in sequenza.

Struttura

Builder interface con metodi per costruire parti, ConcreteBuilder che implementa i passi, Director che definisce l'ordine di costruzione, e Product come risultato finale.

Partecipanti:
Builder - interfaccia astratta per creare parti del prodotto
ConcreteBuilder - implementa i passi e tiene traccia della rappresentazione
Director - costruisce l'oggetto usando l'interfaccia Builder
Product - l'oggetto complesso risultante
Esempi di Codice

Builder per Query SQL

Costruzione fluida di query SQL complesse.

TYPESCRIPT
class SQLQuery {
  private selectClause: string = '';
  private fromClause: string = '';
  private whereConditions: string[] = [];
  private orderByClause: string = '';
  private limitClause: string = '';

  toString(): string {
    let query = this.selectClause + ' ' + this.fromClause;
    
    if (this.whereConditions.length > 0) {
      query += ' WHERE ' + this.whereConditions.join(' AND ');
    }
    
    if (this.orderByClause) {
      query += ' ' + this.orderByClause;
    }
    
    if (this.limitClause) {
      query += ' ' + this.limitClause;
    }
    
    return query;
  }
}

class QueryBuilder {
  private query: SQLQuery;

  constructor() {
    this.query = new SQLQuery();
  }

  select(...columns: string[]): this {
    this.query['selectClause'] = `SELECT ${columns.join(', ')}`;
    return this;
  }

  from(table: string): this {
    this.query['fromClause'] = `FROM ${table}`;
    return this;
  }

  where(condition: string): this {
    this.query['whereConditions'].push(condition);
    return this;
  }

  orderBy(column: string, direction: 'ASC' | 'DESC' = 'ASC'): this {
    this.query['orderByClause'] = `ORDER BY ${column} ${direction}`;
    return this;
  }

  limit(count: number): this {
    this.query['limitClause'] = `LIMIT ${count}`;
    return this;
  }

  build(): SQLQuery {
    return this.query;
  }
}

// Utilizzo
const query = new QueryBuilder()
  .select('name', 'email', 'age')
  .from('users')
  .where('age > 18')
  .where('active = true')
  .orderBy('name', 'ASC')
  .limit(10)
  .build();

console.log(query.toString());
// SELECT name, email, age FROM users WHERE age > 18 AND active = true ORDER BY name ASC LIMIT 10

Builder per Oggetti Complessi

Costruzione di un hamburger con opzioni multiple.

TYPESCRIPT
class Hamburger {
  constructor(
    public bun: string,
    public patty: string,
    public cheese?: string,
    public toppings: string[] = [],
    public sauces: string[] = []
  ) {}

  describe(): string {
    let description = `${this.bun} bun with ${this.patty} patty`;
    if (this.cheese) description += `, ${this.cheese} cheese`;
    if (this.toppings.length) description += `, toppings: ${this.toppings.join(', ')}`;
    if (this.sauces.length) description += `, sauces: ${this.sauces.join(', ')}`;
    return description;
  }
}

class HamburgerBuilder {
  private bun: string = 'regular';
  private patty: string = 'beef';
  private cheese?: string;
  private toppings: string[] = [];
  private sauces: string[] = [];

  setBun(bun: string): this {
    this.bun = bun;
    return this;
  }

  setPatty(patty: string): this {
    this.patty = patty;
    return this;
  }

  addCheese(cheese: string): this {
    this.cheese = cheese;
    return this;
  }

  addTopping(topping: string): this {
    this.toppings.push(topping);
    return this;
  }

  addSauce(sauce: string): this {
    this.sauces.push(sauce);
    return this;
  }

  build(): Hamburger {
    return new Hamburger(
      this.bun,
      this.patty,
      this.cheese,
      this.toppings,
      this.sauces
    );
  }
}

// Utilizzo
const burger = new HamburgerBuilder()
  .setBun('sesame')
  .setPatty('chicken')
  .addCheese('cheddar')
  .addTopping('lettuce')
  .addTopping('tomato')
  .addTopping('onion')
  .addSauce('mayo')
  .addSauce('ketchup')
  .build();

console.log(burger.describe());
Esempi nel Mondo Reale
StringBuilder in Java - costruzione efficiente di stringhe
HTTP Request Builders - librerie come Axios, Fetch per costruire richieste
Form Builders - costruzione di form HTML complessi
Document Builders - creazione di PDF, Word, Excel con configurazioni complesse
Configuration Builders - costruzione di oggetti di configurazione (webpack, babel)
Quando Usarlo
Quando un oggetto ha molti parametri opzionali nel costruttore
Quando vuoi creare diverse rappresentazioni dello stesso oggetto
Quando la costruzione richiede passi complessi
Quando vuoi isolare il codice di costruzione dalla logica business
Quando NON Usarlo
Quando l'oggetto è semplice con pochi parametri
Quando tutti i parametri sono obbligatori
Quando la complessità del builder non è giustificata