No tienes acceso a esta clase

¡Continúa aprendiendo! Únete y comienza a potenciar tu carrera

Builder: pros y contras

21/27
Recursos

¿Cuáles son las ventajas del patrón Builder?

El patrón Builder es una herramienta valiosa que aporta diversas ventajas al desarrollo de software. Este patrón permite construir objetos de manera flexible y modular, lo que puede resultar en código más ordenado y mantenible. Aquí algunas de sus principales ventajas:

  • Permite construir objetos de forma paso a paso, aplazando pasos o utilizando recursividad.
  • Retorna la misma instancia para encadenar métodos, permitiendo gran flexibilidad.
  • Facilita el uso del mismo proceso para crear diferentes representaciones de productos.
  • Permite aislar configuraciones de construcción en un solo lugar, facilitando su mantenimiento.
  • Nuevas configuraciones se agregan sin alterar las existentes, promoviendo la extensibilidad.

Estas características hacen del patrón Builder una opción excelente para gestionar la creación de objetos complejos en tus proyectos.

¿Qué desventajas presenta el patrón Builder?

Aunque el patrón Builder ofrece múltiples beneficios, también tiene algunas desventajas que deben considerarse:

  • Generación de mucho código genérico para cada nuevo producto, lo que puede ser laborioso.
  • Posibilidad de mutación del objeto producto, que podría ser riesgosa si no se controla adecuadamente.

El uso responsable de este patrón y un buen diseño de la solución ayudan a mitigar estos inconvenientes, garantizando un uso eficiente del patrón Builder.

¿Cuándo debería usarse el patrón Builder?

Este patrón es especialmente útil en situaciones donde se busca un control más preciso sobre la construcción de objetos complejos. Algunos casos concretos donde es recomendable su utilización incluyen:

  1. Evitar constructores telescópicos que reciban múltiples parámetros innecesarios.
  2. Necesidad de diferentes representaciones de un mismo producto; por ejemplo, un vehículo y su manual.
  3. Requerir un control detallado paso a paso del proceso de creación de un objeto.

El patrón Builder es también popular en la construcción de consultas para bases de datos, donde es necesario construir paso a paso cadenas complejas adaptadas a diferentes sistemas de gestión de bases de datos.

¿Qué reto se propone al finalizar el estudio del patrón Builder?

Un reto práctico siempre es una excelente manera de consolidar conocimientos. En el contexto de aprender sobre el patrón Builder, se propone crear una línea de producción para un tipo de vehículo específico, como un Hatchback, utilizando las herramientas y métodos apropiados. Además, se invita a desarrollar una variante deportiva, promoviendo la creatividad y la adaptación del código.

La implementación de este reto no solo refuerza la comprensión del patrón Builder, sino que también permite experimentar con la creación y adaptación de nuevas versiones. Comparte tus logros y modificaciones con la comunidad, promoviendo el aprendizaje colaborativo y la mejora continua en el diseño de software.

Aportes 9

Preguntas 0

Ordenar por:

¿Quieres ver más aportes, preguntas y respuestas de la comunidad?

Aunque en general las clases me han parecido excelentes en el caso del patrón builder no me gustó como se explicó desde el inicio mezclado con factory, builder realmente se limita a la creación de objetos complejos, constructores telescópicos con muchos parámetros y/o la sobrecarga de muchos constructores, hubiera sido bueno primero ver el builder puro, luego la implementación builder+factory y así tener claros ambos conceptos. Gracias por las excelentes clases.

Le hice un custom a la solucion de la prueba, donde por medio de la funcion que se inicializadora, agrega dinamicamente las caracteristicas de un fordFiesta HB de esta forma permite seguir creando mas carros HB con las caracteristicas que se requieran:


interface Carro{
    edition : string;
    model:string;
    airBags: number;
    color:string;
}


interface HBProductionLine{
    car: Carro;
    internalModel: string;
    setAirBags(airbags:number) :any;
    setColor(color:string):any;
    setEdition(edition:string):any;
    resetProductLine():void
    build():Carro
}

type ModelCar = {model:string}

class HBFordFiestaProductionLine implements HBProductionLine{
    car: Carro = {
        edition : "",
        model:"",
        airBags: 0,
        color:""
    };
    internalModel: string = "";
    constructor({model}: ModelCar){
        this.car.model = model;
        this.setInternalModel(model);
        this.resetProductLine()
    }
    setAirBags(airbags: number) {
        this.car.airBags = airbags;
    }
    setColor(color: string) {
        this.car.color = color;
    }
    setEdition(edition: string) {
        this.car.edition = edition;
    }
    setInternalModel(model:string){
        this.internalModel = model;
    }
    resetProductLine(): void {
       if(this.internalModel){

       }
    }
    build(): Carro {
       return this.car;
    }
    
}


class CarroFordHB implements Carro{
    edition: string;
    model: string;
    airBags: number;
    color: string;
    constructor(edition: string, model: string, airBags: number, color: string){
        this.airBags = airBags;
        this.model = model;
        this.color = color;
        this.edition = edition;
    }
}

class Orquestator {
    buildCarFordHB(car: CarroFordHB): CarroFordHB {
        const HBFordFiesta = new HBFordFiestaProductionLine({model: car.model})
        HBFordFiesta.setAirBags(car.airBags);
        HBFordFiesta.setColor(car.color);
        HBFordFiesta.setEdition(car.edition);
        return HBFordFiesta.build()
    }
}

const main = (orq:Orquestator) =>{
    const carFordFiesta = new CarroFordHB("st","2018",2,"grey");
    const carBuilderFord = orq.buildCarFordHB({airBags: carFordFiesta.airBags, color: carFordFiesta.color, edition: carFordFiesta.edition, model: carFordFiesta.model})
    console.log(carBuilderFord) // { edition: 'st', model: '2018', airBags: 2, color: 'grey' }
}

main(new Orquestator())

Comparto mi solución n.n

type AvailableColors =
    | "red"
    | "black"
    | "gray"
    | "blue"
    | "sky blue"
    | "white"
    | "default";

// STEP 1
interface CarProductionLine {
    setAirBags(howMany: number): CarProductionLine;
    setColor(color: AvailableColors): CarProductionLine;
    setEdition(edition: EditionsType): CarProductionLine;
    resetProductionLine(): void;
}

// STEP 2
type CarCatalog = "mastodon" | "rhino";
type ConstructorParams = { modelToCustomizeInLine: CarCatalog };
type EditionsType = "cvt" | "signature" | "sport" | "default";
class SedanProductionLine implements CarProductionLine {
    private sedanCar!: BaseCar;
    private modelToCustomizeInLine!: CarCatalog;

    constructor({ modelToCustomizeInLine }: ConstructorParams) {
        this.setModelToBuild(modelToCustomizeInLine);
        this.resetProductionLine();
    }

    setAirBags(howMany: number): SedanProductionLine {
        this.sedanCar.airBags = howMany;
        return this;
    }

    setColor(color: AvailableColors): SedanProductionLine {
        this.sedanCar.color = color;
        return this;
    }

    setEdition(edition: EditionsType): SedanProductionLine {
        this.sedanCar.edition = edition;
        return this;
    }

    setModelToBuild(model: CarCatalog) {
        this.modelToCustomizeInLine = model;
    }

    resetProductionLine() {
        this.sedanCar =
            this.modelToCustomizeInLine === "mastodon"
                ? new MastodonSedanCar()
                : new RhinoSedanCar();
    }

    build(): BaseCar {
        const sedanCar = this.sedanCar;
        this.resetProductionLine();
        return sedanCar;
    }
}
class HatchBackProductionLine implements CarProductionLine {
    private hatchbackCar!: BaseCar;
    private modelToCustomizeInLine!: CarCatalog;

    constructor({ modelToCustomizeInLine }: ConstructorParams) {
        this.setModelToBuild(modelToCustomizeInLine);
        this.resetProductionLine();
    }

    setAirBags(howMany: number): HatchBackProductionLine {
        this.hatchbackCar.airBags = howMany;
        return this;
    }

    setColor(color: AvailableColors): HatchBackProductionLine {
        this.hatchbackCar.color = color;
        return this;
    }

    setEdition(edition: EditionsType): HatchBackProductionLine {
        this.hatchbackCar.edition = edition;
        return this;
    }

    setModelToBuild(model: CarCatalog) {
        this.modelToCustomizeInLine = model;
    }

    resetProductionLine() {
        this.hatchbackCar =
            this.modelToCustomizeInLine === "mastodon"
                ? new MastodonHatchbackCar()
                : new RhinoHatchbackCar();
    }

    build(): BaseCar {
        const hatchbackCar = this.hatchbackCar;
        this.resetProductionLine();
        return hatchbackCar;
    }
}

// STEP 3
class BaseCar {
    private _edition!: EditionsType;
    private _model!: string;
    private _airBags: number = 2;
    private _color: AvailableColors = "black";

    set airBags(howMany: number) {
        this._airBags = howMany;
    }

    set color(color: AvailableColors) {
        this._color = color;
    }

    set edition(edition: EditionsType) {
        this._edition = edition;
    }

    set model(model: string) {
        this._model = model;
    }
}

class MastodonSedanCar extends BaseCar {
    constructor() {
        super();
        this.model = "sedan";
    }
}
class RhinoSedanCar extends BaseCar {
    constructor() {
        super();
        this.model = "sedan";
    }
}

class MastodonHatchbackCar extends BaseCar {
    constructor() {
        super();
        this.model = "hatchback";
    }
}
class RhinoHatchbackCar extends BaseCar {
    constructor() {
        super();
        this.model = "hatchback";
    }
}

// STEP 4
export class Director {
    private productionLine!: CarProductionLine;

    setProductionLine(productionLine: CarProductionLine) {
        this.productionLine = productionLine;
    }

    constructCvtEdition(): void {
        this.productionLine
            .setAirBags(4)
            .setColor("sky blue")
            .setEdition("cvt");
    }

    constructSignatureEdition(): void {
        this.productionLine
            .setAirBags(8)
            .setColor("gray")
            .setEdition("signature");
    }

    constructSportEdition(): void {
        this.productionLine.setAirBags(1).setColor("white").setEdition("sport");
    }
}

type modelCar = "sedan" | "hatchback";
function appBuilder(director: Director, model: modelCar) {
    const productionsLines = {
        sedan: SedanProductionLine,
        hatchback: HatchBackProductionLine,
    };
    const ProductionLine = productionsLines[model];

    const mastodonProductionLine = new ProductionLine({
        modelToCustomizeInLine: "mastodon",
    });

    director.setProductionLine(mastodonProductionLine);

    director.constructCvtEdition();
    const mastodonModelCvt = mastodonProductionLine.build();
    console.log(mastodonModelCvt);

    director.constructSignatureEdition();
    const mastodonModelSignature = mastodonProductionLine.build();
    console.log(mastodonModelSignature);

    director.constructSportEdition();
    const mastodonModelSport = mastodonProductionLine.build();
    console.log(mastodonModelSport);
}

appBuilder(new Director(), "hatchback");

Comparto mi codigo TS

// Types
type BaseColors = 'blue' | 'black' | 'white';
type Editions = 'CVT' | 'Signature' | 'Sport';
type CarModel = 'mastodon' | 'rhino';

//  Interface productionLine
interface CarProductionLine {
    setAirbags(number: number): CarProductionLine;
    setColor(color: BaseColors): CarProductionLine;
    setEdition(edition: Editions): CarProductionLine;
    resetProductionLine(): void;
    build(): BaseCar;
}


// BaseCar class
class BaseCar {
    private _airbags: number = 2;
    private _color: BaseColors = 'white';
    private _edition!: Editions;
    private _model!: string;

    set airbags(number: number) {
        this._airbags = number;
    }

    set color(color: BaseColors) {
        this._color = color;
    }

    set edition(edition: Editions) {
        this._edition = edition;
    }

    set model(model: string) {
        this._model = model;
    }
}

// Concrete model Class
class MastodonHatchbackCar extends BaseCar {
    constructor() {
        super()
        this.model = 'Hatchback';
    }
}

class RhinoHatchbackCar extends BaseCar {
    constructor() {
        super();
        this.model = 'Hatchback';
    }
}

// HatchBack concrete production Line
class HatchbackProductionLine implements CarProductionLine {

    private _hatchbackCar!: BaseCar;
    private _modelToBuild!: CarModel;

    constructor(carModel: CarModel) {
        this.modelTobuild(carModel);
        this.resetProductionLine();
    }

    setAirbags(number: number): HatchbackProductionLine {
        this._hatchbackCar.airbags = number;
        return this
    }

    setColor(color: BaseColors): HatchbackProductionLine {
        this._hatchbackCar.color = color;
        return this;
    }

    setEdition(edition: Editions): HatchbackProductionLine {
        this._hatchbackCar.edition = edition;
        return this;
    }

    resetProductionLine(): void {
        this._hatchbackCar =
            this._modelToBuild === 'mastodon' ?
                new MastodonHatchbackCar() :
                new RhinoHatchbackCar();
    }

    modelTobuild(carModel: CarModel) {
        this._modelToBuild = carModel;
    }

    build(): BaseCar {
        const HatchbackCar = this._hatchbackCar;
        this.resetProductionLine();
        return HatchbackCar;
    }
}

class Director {

    private _productionLine!: CarProductionLine;

    setProductionLine(productionLine: CarProductionLine) {
        this._productionLine = productionLine;
    }

    constructCvt() {
        this._productionLine
            .setAirbags(4)
            .setColor('blue')
            .setEdition('CVT')            
    };

    constructSignature() {
        this._productionLine
            .setAirbags(3)
            .setColor('black')
            .setEdition('Signature')
    };

    constructSport() {
        this._productionLine
            .setAirbags(2)
            .setEdition('Sport')
            .setColor('blue')
    };
}

// Applications
// mastodon hatchback builder
function hatchBackMastodonBuilder(director: Director) {
    
    if (!director) {
        console.log('--- No director provided ---');
        return;
      }

    const hatchBackMastodonProductionLine = new HatchbackProductionLine('mastodon');

    director.setProductionLine(hatchBackMastodonProductionLine);

    // CVT Construction
    director.constructCvt();
    const mastodonHatchbackCVT = hatchBackMastodonProductionLine.build()
    console.log('--- MASTODON HATCHBACK CVT ---');
    console.log(mastodonHatchbackCVT);
    
    // Signature Construction   
    director.constructSignature();
    const mastodonHatchbackSignature = hatchBackMastodonProductionLine.build()
    console.log('--- MASTODON HATCHBACK SIGNATURE ---');
    console.log(mastodonHatchbackSignature);

    // Sport Construction   
    director.constructSport();
    const mastodonHatchbackSport = hatchBackMastodonProductionLine.build()
    console.log('--- MASTODON HATCHBACK SPORT ---');
    console.log(mastodonHatchbackSport);
}

// rhino hatchback builder
function hatchBackRhinoBuilder(director: Director) {
    
    if (!director) {
        console.log('--- No director provided ---');
        return;
      }

    const hatchBackRhinoProductionLine = new HatchbackProductionLine('rhino');

    director.setProductionLine(hatchBackRhinoProductionLine);

    // CVT Construction
    director.constructCvt();
    const rhinoHatchbackCVT = hatchBackRhinoProductionLine.build()
    console.log('--- RHINO HATCHBACK CVT ---');
    console.log(rhinoHatchbackCVT);
    
    // Signature Construction   
    director.constructSignature();
    const rhinoHatchbackSignature = hatchBackRhinoProductionLine.build()
    console.log('--- RHINO HATCHBACK SIGNATURE ---');
    console.log(rhinoHatchbackSignature);

    // Sport Construction   
    director.constructSport();
    const rhinoHatchbackSport = hatchBackRhinoProductionLine.build()
    console.log('--- RHINO HATCHBACK SPORT ---');
    console.log(rhinoHatchbackSport);
};

// Implementation
hatchBackMastodonBuilder(new Director);
hatchBackRhinoBuilder(new Director);



De los 3 patrones, este fue el que menos entendí desde el inicio no me gusto como se explicó. Me parece que se fue mezclando demasiado, además de que en el video teníamos un código pero luego en el repositorio era otro. En general el curso bien, pero esto sería lo negativo.

Solución 😄…

.
.
.
.

// General steps so as to build products

interface CarProductionLineTS {
  setAirBags(howMany: number): CarProductionLineTS;
  setColor(color: AvailableColors): CarProductionLineTS;
  setEdition(edition: string): CarProductionLineTS;
  resetProductionLine(): void;
}

// Concrete builders subclasses

type CarCatalog = "mastodon" | "rhino";
type ConstructorParams = { model: CarCatalog };
class SedanProductionLineTS implements CarProductionLineTS {
  private sedanCar!: CarTS;
  private internalModel!: CarCatalog;

  constructor({ model }: ConstructorParams) {
    this.setInternalModel(model);
    this.resetProductionLine();
  }

  setAirBags(howMany: number): SedanProductionLineTS {
    this.sedanCar.airBags = howMany;
    return this;
  }

  setColor(color: AvailableColors): SedanProductionLineTS {
    this.sedanCar.color = color;
    return this;
  }

  setEdition(edition: string): SedanProductionLineTS {
    this.sedanCar.edition = edition;
    return this;
  }

  setInternalModel(model: CarCatalog) {
    this.internalModel = model;
  }

  setModel() {
    this.sedanCar.model = "sedan";
  }

  resetProductionLine(): void {
    this.sedanCar =
      this.internalModel === "mastodon"
        ? new MastodonCarTS()
        : new RhinoCarTS();
  }

  build(): CarTS {
    this.setModel();
    const sedanCar = this.sedanCar;
    this.resetProductionLine();
    return sedanCar;
  }
}

class HatchbackProductionLineTS implements CarProductionLineTS {
  private hatchbackCar!: CarTS;
  private internalModel!: CarCatalog;

  constructor({ model }: ConstructorParams) {
    this.setInternalModel(model);
    this.resetProductionLine();
  }

  setAirBags(howMany: number): HatchbackProductionLineTS {
    this.hatchbackCar.airBags = howMany;
    return this;
  }

  setColor(color: AvailableColors): HatchbackProductionLineTS {
    this.hatchbackCar.color = color;
    return this;
  }

  setEdition(edition: string): HatchbackProductionLineTS {
    this.hatchbackCar.edition = edition;
    return this;
  }

  setInternalModel(model: CarCatalog) {
    this.internalModel = model;
  }

  setModel() {
    this.hatchbackCar.model = "hatchback";
  }

  resetProductionLine(): void {
    this.hatchbackCar =
      this.internalModel === "mastodon"
        ? new MastodonCarTS()
        : new RhinoCarTS();
  }

  build(): CarTS {
    this.setModel();
    const hatchbackCar = this.hatchbackCar;
    this.resetProductionLine();
    return hatchbackCar;
  }
}

// Implement product classes, these ones could not belong to the same interface

type AvailableColors = "red" | "black" | "gray" | "blue";
class CarTS {
  private _edition!: string;
  private _model!: string;
  private _airBags: number = 2;
  private _color: AvailableColors = "black";

  set airBags(howMany: number) {
    this._airBags = howMany;
  }

  set color(color: AvailableColors) {
    this._color = color;
  }

  set edition(edition: string) {
    this._edition = edition;
  }

  set model(model: string) {
    this._model = model;
  }
}

class MastodonCarTS extends CarTS {
  constructor() {
    super();
  }
}

class RhinoCarTS extends CarTS {
  constructor() {
    super();
  }
}

// Implement director class

class DirectorTS {
  private productionLine!: CarProductionLineTS;

  setProductionLine(productionLine: CarProductionLineTS) {
    this.productionLine = productionLine;
  }

  constructCvtEdition(): void {
    this.productionLine.setAirBags(4).setColor("blue").setEdition("CVT");
  }

  constructSignatureEdition(): void {
    this.productionLine.setAirBags(8).setColor("gray").setEdition("Signature");
  }

  constructSportEdition(): void {
    this.productionLine.setAirBags(12).setColor("black").setEdition("Sport");
  }
}

function appBuilderTS(director: DirectorTS) {
  const mastodonSedanProductionLine = new SedanProductionLineTS({
    model: "mastodon",
  });

  director.setProductionLine(mastodonSedanProductionLine);
  director.constructCvtEdition();
  const mastodonSedanCvt = mastodonSedanProductionLine.build();
  console.log(mastodonSedanCvt);

  director.constructSignatureEdition();
  const mastodonSedanSignature = mastodonSedanProductionLine.build();
  console.log(mastodonSedanSignature);

  director.constructSportEdition();
  const mastodonSedanSport = mastodonSedanProductionLine.build();
  console.log(mastodonSedanSport);

  const mastodonHatchbackProductionLine = new HatchbackProductionLineTS({
    model: "mastodon",
  });

  director.setProductionLine(mastodonHatchbackProductionLine);
  director.constructCvtEdition();
  const mastodonHatchbackCvt = mastodonHatchbackProductionLine.build();
  console.log(mastodonHatchbackCvt);

  director.constructSignatureEdition();
  const mastodonHatchbackSignature = mastodonHatchbackProductionLine.build();
  console.log(mastodonHatchbackSignature);

  director.constructSportEdition();
  const mastodonHatchbackSport = mastodonHatchbackProductionLine.build();
  console.log(mastodonHatchbackSport);

  const rhinoSedanProductionLine = new SedanProductionLineTS({
    model: "rhino",
  });

  director.setProductionLine(rhinoSedanProductionLine);
  director.constructCvtEdition();
  const rhinoSedanCvt = rhinoSedanProductionLine.build();
  console.log(rhinoSedanCvt);

  director.constructSignatureEdition();
  const rhinoSedanSignature = rhinoSedanProductionLine.build();
  console.log(rhinoSedanSignature);

  director.constructSportEdition();
  const rhinoSedanSport = rhinoSedanProductionLine.build();
  console.log(rhinoSedanSport);

  const rhinoHatchbackProductionLine = new HatchbackProductionLineTS({
    model: "rhino",
  });

  director.setProductionLine(rhinoHatchbackProductionLine);
  director.constructCvtEdition();
  const rhinoHatchbackCvt = rhinoHatchbackProductionLine.build();
  console.log(rhinoHatchbackCvt);

  director.constructSignatureEdition();
  const rhinoHatchbackSignature = rhinoHatchbackProductionLine.build();
  console.log(rhinoHatchbackSignature);

  director.constructSportEdition();
  const rhinoHatchbackSport = rhinoHatchbackProductionLine.build();
  console.log(rhinoHatchbackSport);
}

appBuilderTS(new DirectorTS());
```js //*PASO 1. Declarar clase base que definirá los pasos generales de // creación del producto (línea de producción de cada familia).*/ class CarProductionLine { setAirBags(airBags){ throw new Error('Method not implemented'); } setColor(color){ throw new Error('Method not implemented'); } * setEdition(edition){ throw new Error('Method not implemented'); } resetProductionLine(){ throw new Error('Method not implemented'); } } class HatchbackProductionLine extends CarProductionLine{ constructor({model}){ super();//Usa el costructor de la clase padre; lo que esta contenga es ahora parte de // la clase hija, para construir un objeto de la clase HatchbackProductionLine() this.setInternalModel(model)//Propiedad interna para aclarar si es Production Line // de Mastodon o Rhino. this.resetProductionLine();//Aquí llamamos la función resetProductionLine() que está // abajo. }//Con este llamamiento, hay una asignacion a hatchbackCar y así podrán llamarse todos los // métodos con hatchbackCar. setAirBags(airBags){ this.hatchbackCar.airBags = airBags; return this;//Retornamos la misma instancia de la clase para retornar el objeto del // builder (clase HatchbackProductionLine). } setColor(color){ this.hatchbackCar.color = color; return this; } setEdition(edition){ this.hatchbackCar.edition = edition; return this; } setInternalModel(model){//agragamos la función vinculada a la propiedad interna // this.setInternalModel this.internalModel = model;//variable que corresponde a la instancia de la línea de // producción a la cual le asignamos el modelo } setModel() { this.hatchbackCar.model = 'hatchback'; } resetProductionLine(){ this.hatchbackCar = this.internalModel === 'mastodon' ? new MastodonCar() : new RhinoCar();//Operador ternario }//Vamos a trabajar con un modelo(this.internalModel). Si el modelo es un mastodon, genera // un nuevo MastodonCar. De lo contario, RhinoCar. build(){//el diagrama dice que agreguemos el método build; regresa la instancia ya // configurada, con su representación Signature o CVT this.setModel()//no tenemos que pasarle nada porque nuestra línea de producción es // específicamente para hatchback, según la clase del paso 2 const hatchbackCar = this.hatchbackCar;//La instancia de la línea de producción en este // momento tiene un hatchbackCar. Quiero que me lo des. Guardamos hatchbackCar en la // variable hatchbackCar this.resetProductionLine();//luego voy a resetear la línea de producción. Si no le he // dicho que cambie el modelo, seguirá produciendo Mastodon. return hatchbackCar;//retornamos hatchbackCar, que es el producto finalizado. Estamos // al final de la línea de producción para recibir nuestro carro configurado. } } /*PASO 3: Implementar clases de producto, productos concretos que pueden ser retornados por los builders. No tienen que seguir una clase base.*/ class Car {//clase Car con 4 propiedades constructor() { this.edition = '';//definirá si es CVT o Signature this.model = '';//definirá si es Hatchback o hatchback this.airBags = 4;//bolsas de aire predeterminadas this.color = 'blue';//color predeterminado }//la clase tendrá a continuación métodos para que asignemos los valores de las 4 // propiedades set edition(edition) { this._edition = edition; } set model(model) { this._model = model; } set airBags(airBags) { this._airBags = airBags; } set color(color) { this._color = color; } } class MastodonCar extends Car { constructor() { super(); } }//clase de producto concreto, extiende clase Car y con super hereda el constructor de la clase padre. class RhinoCar extends Car { constructor() { super(); } }//clase de producto concreto, extiende clase Car y con super hereda el constructor de la clase // padre. /*PASO 4 Implementar una clase directora que conocerá el orden en el que se llamarán los pasos de creación de cada configuración (los pasos necesarios para la versión Signature y CVT).*/ class Director { setProductionLine(productionLine) { this.productionLine = productionLine; } constructCVTEdition() { this.productionLine.setAirBags(4).setColor('blue').setEdition('CVT'); } constructSignatureEdition() { this.productionLine.setAirBags(8).setColor('black').setEdition('Signature'); } constructSportEdition() { this.productionLine.setAirBags(6).setColor('red').setEdition('Sport'); } } function appBuilder(director) { const mastodonHatchbackProductionLine = new HatchbackProductionLine({model: 'mastodon'}); //esto se conecta con this.setInternalModel(model) del paso 2 para setear la información. director.setProductionLine(mastodonHatchbackProductionLine); //con mastodonHatchbackProductionLine le estamos pasado al director la línea de producción // que teine los pasos de creación de los productos. director.constructCVTEdition(); const mastodonHatchbackCVT = mastodonHatchbackProductionLine.build(); console.log(mastodonHatchbackCVT); director.constructSignatureEdition(); const mastodonHatchbackSignature = mastodonHatchbackProductionLine.build(); console.log(mastodonHatchbackSignature); director.constructSportEdition(); const mastodonHatchbackSport = mastodonHatchbackProductionLine.build(); console.log(mastodonHatchbackSport); } appBuilder(new Director()); ```//\*PASO 1. Declarar clase base que definirá los pasos generales de // creación del producto (línea de producción de cada familia).\*/class CarProductionLine {    setAirBags(airBags){        throw new Error('Method not implemented');    }    setColor(color){        throw new Error('Method not implemented');    } \*  setEdition(edition){    throw new Error('Method not implemented'); }    resetProductionLine(){        throw new Error('Method not implemented');    }}class HatchbackProductionLine extends CarProductionLine{    constructor({model}){        super();//Usa el costructor de la clase padre; lo que esta contenga es ahora parte de         // la clase hija, para construir un objeto de la clase HatchbackProductionLine()        this.setInternalModel(model)//Propiedad interna para aclarar si es Production Line         // de Mastodon o Rhino.        this.resetProductionLine();//Aquí llamamos la función resetProductionLine() que está         // abajo.    }//Con este llamamiento, hay una asignacion a hatchbackCar y así podrán llamarse todos los     // métodos con hatchbackCar.    setAirBags(airBags){        this.hatchbackCar.airBags = airBags;        return this;//Retornamos la misma instancia de la clase para retornar el objeto del         // builder (clase HatchbackProductionLine).     }    setColor(color){        this.hatchbackCar.color = color;        return this;    }    setEdition(edition){        this.hatchbackCar.edition = edition;        return this;    }    setInternalModel(model){//agragamos la función vinculada a la propiedad interna     // this.setInternalModel        this.internalModel = model;//variable que corresponde a la instancia de la línea de         // producción a la cual le asignamos el modelo    }    setModel() {        this.hatchbackCar.model = 'hatchback';    }    resetProductionLine(){        this.hatchbackCar = this.internalModel === 'mastodon' ? new MastodonCar() : new RhinoCar();//Operador ternario    }//Vamos a trabajar con un modelo(this.internalModel). Si el modelo es un mastodon, genera     // un nuevo MastodonCar. De lo contario, RhinoCar.    build(){//el diagrama dice que agreguemos el método build; regresa la instancia ya     // configurada, con su representación Signature o CVT        this.setModel()//no tenemos que pasarle nada porque nuestra línea de producción es         // específicamente para hatchback, según la clase del paso 2        const hatchbackCar = this.hatchbackCar;//La instancia de la línea de producción en este         // momento tiene un hatchbackCar. Quiero que me lo des. Guardamos hatchbackCar en la         // variable hatchbackCar        this.resetProductionLine();//luego voy a resetear la línea de producción. Si no le he         // dicho que cambie el modelo, seguirá produciendo Mastodon.        return hatchbackCar;//retornamos hatchbackCar, que es el producto finalizado. Estamos         // al final de la línea de producción para recibir nuestro carro configurado.    }}/\*PASO 3: Implementar clases de producto, productos concretos que pueden ser retornados por los builders. No tienen que seguir una clase base.\*/class Car {//clase Car con 4 propiedades    constructor() {        this.edition = '';//definirá si es CVT o Signature        this.model = '';//definirá si es Hatchback o hatchback        this.airBags = 4;//bolsas de aire predeterminadas        this.color = 'blue';//color predeterminado    }//la clase tendrá a continuación métodos para que asignemos los valores de las 4     // propiedades    set edition(edition) {        this.\_edition = edition;    }    set model(model) {        this.\_model = model;    }    set airBags(airBags) {        this.\_airBags = airBags;    }    set color(color) {        this.\_color = color;    }}class MastodonCar extends Car {    constructor() {        super();    }}//clase de producto concreto, extiende clase Car y con super hereda el constructor de la clase padre.class RhinoCar extends Car {    constructor() {        super();    }}//clase de producto concreto, extiende clase Car y con super hereda el constructor de la clase // padre./\*PASO 4 Implementar una clase directora que conocerá el orden en el que se llamarán los pasos de creación de cada configuración (los pasos necesarios para la versión Signature y CVT).\*/class Director {    setProductionLine(productionLine) {        this.productionLine = productionLine;    }    constructCVTEdition() {        this.productionLine.setAirBags(4).setColor('blue').setEdition('CVT');    }    constructSignatureEdition() {        this.productionLine.setAirBags(8).setColor('black').setEdition('Signature');    }    constructSportEdition() {        this.productionLine.setAirBags(6).setColor('red').setEdition('Sport');    }}function appBuilder(director) {    const mastodonHatchbackProductionLine = new HatchbackProductionLine({model: 'mastodon'});    //esto se conecta con this.setInternalModel(model) del paso 2 para setear la información.    director.setProductionLine(mastodonHatchbackProductionLine);    //con mastodonHatchbackProductionLine le estamos pasado al director la línea de producción     // que teine los pasos de creación de los productos.    director.constructCVTEdition();    const mastodonHatchbackCVT = mastodonHatchbackProductionLine.build();    console.log(mastodonHatchbackCVT);     director.constructSignatureEdition();    const mastodonHatchbackSignature = mastodonHatchbackProductionLine.build();    console.log(mastodonHatchbackSignature);     director.constructSportEdition();    const mastodonHatchbackSport = mastodonHatchbackProductionLine.build();    console.log(mastodonHatchbackSport);}appBuilder(new Director());
```ts // clase producto que queremos construir class Car { public edition!: string; public engine!: string; public wheels!: number; public airbags!: number; public color!: string; public gps!: boolean; public showSpecifications(): void { console.log( `Engine: ${this.engine}, Wheels: ${this.wheels}, Color: ${this.color}, GPS: ${this.gps}, Editon: ${this.edition}, Airbags: ${this.airbags}.`, ); } } // interfaz para el builder interface CarBuilder { setEdition(edition: string): this; setEngine(engine: string): this; setWheels(wheels: number): this; setAirBags(airbags: number): this; setColor(color: string): this; setGPS(gps: boolean): this; build(): Car; reset(): void; } // Implementación concreta del Builder class ConcreteCarBuilder implements CarBuilder { private car: Car; constructor() { this.car = new Car(); } public setEdition(edition: string): this { this.car.edition = edition; return this; } public setEngine(engine: string): this { this.car.engine = engine; return this; } public setWheels(wheels: number): this { this.car.wheels = wheels; return this; } public setAirBags(airbags: number): this { this.car.airbags = airbags; return this; } public setColor(color: string): this { this.car.color = color; return this; } public setGPS(gps: boolean): this { this.car.gps = gps; return this; } public reset(): void { this.car = new Car(); } public build(): Car { const builtCar = this.car; this.car = new Car(); // reinicia el car para próximas construcciones return builtCar; } } // Director (opcional) facilita la construcción predefinida class CarDirector { private builder: CarBuilder; constructor(builder: CarBuilder) { this.builder = builder; } public constructSportCar(): Car { return this.builder .setEdition("sport") .setAirBags(6) .setEngine("v8") .setWheels(4) .setColor("red") .setGPS(true) .build(); } public constructSuvCar(): Car { return this.builder .setEdition("suv") .setAirBags(8) .setEngine("diesel") .setWheels(4) .setColor("black") .setGPS(false) .build(); } public constructHatchbackCar(): Car { return this.builder .setEdition("hatchback") .setAirBags(4) .setColor("yellow") .setEngine("v6") .setWheels(4) .setGPS(true) .build(); } } // Uso del patrón Builder const builder = new ConcreteCarBuilder(); const director = new CarDirector(builder); const sport = director.constructSportCar(); sport.showSpecifications(); const suv = director.constructSuvCar(); suv.showSpecifications(); const hatchback = director.constructHatchbackCar(); hatchback.showSpecifications(); // construcción personalizada sin el director const customCar = builder .setEngine("electric") .setWheels(3) .setColor("white") .setGPS(true) .build(); customCar.showSpecifications(); ```
He utilizado la librería \[Day.js]\([Day.js · 2kB JavaScript date utility library](https://day.js.org/)) y creo que utiliza el patrón Builder, porque puedes concatenar métodos para tener un resultado final ```js import dayjs from 'dayjs' const todayMorning = dayjs().hour(5).minute(30).second(20).toString() console.log(todayMorning) // 'Sun, 03 Dec 2023 15:30:20 GMT' ```