Super tediosa la implementación en JS
Introduccion
Patrones de Diseño Creacionales en Software
Patrones Creacionales: Singleton, Factory, Abstract Factory, Builder, Prototype
Singleton
Patrón Singleton: Implementación y Uso en Programación Orientada a Objetos
Diagrama de Clases del Patrón Singleton en JavaScript
Diferencias entre JavaScript y TypeScript en patrones de diseño
Ventajas y desventajas del patrón Singleton en diseño de software
Factory
Patrones de Diseño: Introducción al Patrón Factory
Patrón Factory: Implementación y Detalles Esenciales
Implementación del Patrón Factory Method en JavaScript
Comparación del Patrón Factory en JavaScript y TypeScript
Patrón Factory: Ventajas y Desventajas en Desarrollo de Software
Abstract Factory
Patrón Abstract Factory: Estrategia para Múltiples Fábricas de Coches
Patrones de Diseño: Abstract Factory en Producción de Coches
Implementación del patrón Abstract Factory en JavaScript
Diferencias entre JavaScript y TypeScript en el patrón Abstract Factory
Patrón Abstract Factory: Ventajas y Desventajas
Builder
Patrón Builder: Diseño y Aplicación en Producción de Vehículos
Patrón Builder: Análisis de Diagrama y Clases Relacionadas
Implementación del Patrón Builder en Producción de Coches
Comparación del Patrón Builder en JavaScript vs TypeScript
Patrón Builder: Ventajas, Desventajas y Aplicaciones Prácticas
Prototype
Patrón Prototype: Clonación de Objetos en Diseño de Software
Patrón Prototype en JavaScript y TypeScript
Implementación del Patrón Prototype en JavaScript
Comparación de Prototype en JavaScript y TypeScript
Patrón Prototype: Ventajas y Desafíos en Diseño de Software
Conclusiones
Patrones Creacionales en Diseño de Software
No tienes acceso a esta clase
¡Continúa aprendiendo! Únete y comienza a potenciar tu carrera
El patrón Builder es una de las técnicas más efectivas para manejar construcciones complejas en programación. En este contexto, implementamos el patrón Builder en la línea de producción de coches. Su objetivo principal es simplificar el proceso de creación de objetos complejos mediante la separación del proceso de creación en pasos pequeños y manejables. Aquí encontrarás los detalles de cómo se implementa este patrón en la producción de vehículos, utilizando específicamente JavaScript.
El primer paso para implementar el patrón Builder es definir una clase base o una interfaz que estipule los pasos generales para construir productos específicos. Para el contexto de producción de coches, definimos la clase CarProductionLine
con métodos esenciales como la configuración de las bolsas de aire, el color y la edición del automóvil. Aquí un ejemplo de código:
class CarProductionLine {
setAirbags(howMany) {
throw new Error('Método no implementado');
}
setColor(color) {
throw new Error('Método no implementado');
}
setEdition(edition) {
throw new Error('Método no implementado');
}
resetProductionLine() {
throw new Error('Método no implementado');
}
}
Cada método en nuestra clase base arroja un error si no se implementa, asegurando que todas las clases constructoras deben definir estos métodos.
El segundo paso es implementar builders concretos para ofrecer diferentes implementaciones de los pasos definidos en la clase base. Por ejemplo, en la fábrica de sedanes creamos la clase SedanProductionLine
que proporciona implementaciones concretas para el proceso de producción:
class SedanProductionLine extends CarProductionLine {
setAirbags(howMany) {
this.sedanCar.airbags = howMany;
return this;
}
setColor(color) {
this.sedanCar.color = color;
return this;
}
setEdition(edition) {
this.sedanCar.edition = edition;
return this;
}
resetProductionLine() {
const model = this.internalModel;
this.sedanCar = model === 'Mastodon' ? new Mastodon() : new Rhino();
}
}
Gracias al uso de this
, podemos encadenar métodos de manera elegante y eficiente, facilitando la personalización del vehículo.
La clase director tiene la responsabilidad de conocer y definir el proceso exacto para construir las variaciones del producto. Así, se establece un orden en los pasos de construcción. Aquí se muestra cómo configuramos un director para gestionar la construcción de diferentes ediciones de coches:
class Director {
setBuilder(builder) {
this.productionLine = builder;
}
constructCVTEdition() {
this.productionLine.setAirbags(4).setColor('Azul').setEdition('CVT');
}
constructSignatureEdition() {
this.productionLine.setAirbags(8).setColor('Rojo').setEdition('Signature');
}
}
El director garantiza que se sigan los pasos en un orden particular y permite la reutilización del mismo builder para diferentes configuraciones de productos.
Impulsado por el director, el builder es capaz de ensamblar el producto y entregarlo ya configurado, manteniendo el estado del proceso y asegurando que el producto esté listo para ser utilizado:
const director = new Director();
const mastodonProductionLine = new SedanProductionLine('Mastodon');
director.setBuilder(mastodonProductionLine);
director.constructCVTEdition();
const mastodonCar = mastodonProductionLine.build();
console.log(mastodonCar);
El método build
retorna el coche resultado de la configuración asignada, mostrando cómo los diversos métodos de construcción afectan al producto final.
Implementar el patrón Builder no solo organiza el código sino que permite manejar de manera flexible la creación de objetos complejos, reduciendo el riesgo de errores y ofreciendo un código más mantenible. Mediante la separación de las acciones de construcción en varias clases, incluyendo los builders y el director, las responsabilidades están claramente definidas y desacopladas. Además, esta estructura es especialmente valiosa cuando se espera que los productos entren y salgan del proceso de producción de diferentes maneras en función de ciertos parámetros.
¡Ahora es tu turno de aplicar el patrón Builder y ver cómo se adapta a tus necesidades de desarrollo! Recuerda, la práctica es clave para dominar cualquier metodología de programación. ¡Sigue adelante y sigue construyendo!
Aportes 16
Preguntas 1
Super tediosa la implementación en JS
Lo veo muy util para abstraer la complejidad de la construcción de objetos cuyo constructor requiere varios parámetros y no siempre son obligatorios. Una clase Builder, abstrae esta complejidad y facilita el uso del objeto. En este ejemplo más de la vida real he imaginado como pagar la liquidación de un empleado, en este caso hay varios factores que intervienen en el cálculo. Aquí es donde entra el patrón:
class Liquidation {
private basicSalaryMontly: number;
private monthsWorked: number;
private pendingHolyDays: number;
private premiumEndYear: number;
private premiumMediumYear: number;
private layoffs: number;
/**
*
*/
constructor(
basicSalaryMontly: number,
monthsWorked: number,
pendingHolyDays: number,
premiumEndYear: number,
premiumMediumYear: number,
layoffs: number) {
this.basicSalaryMontly = basicSalaryMontly;
this.monthsWorked = monthsWorked;
this.pendingHolyDays = pendingHolyDays;
this.premiumEndYear = premiumEndYear;
this.premiumMediumYear = premiumMediumYear;
this.layoffs = layoffs;
}
Calculate() {
const totalPendingHollyDays = ((this.basicSalaryMontly / 30) * 365) * this.pendingHolyDays;
const total = (this.basicSalaryMontly * this.monthsWorked) + totalPendingHollyDays + this.premiumEndYear + this.premiumMediumYear + this.layoffs;
console.log("Vacaciones pendientes: ", totalPendingHollyDays);
console.log("Liquidación Total ", total);
}
}
class LiquidationBuilder {
private basicSalaryMontly: number;
private monthsWorked: number;
private pendingHolyDays: number;
private premiumEndYear: number;
private premiumMediumYear: number;
private layoffs: number;
setBasicSalaryMonth(basicSalaryMontly: number): LiquidationBuilder {
this.basicSalaryMontly = basicSalaryMontly;
return this;
}
setMothsWorked(monthsWorked: number): LiquidationBuilder {
this.monthsWorked = monthsWorked;
return this;
}
setPendingHollyDays(pendingHolyDays: number): LiquidationBuilder {
this.pendingHolyDays = pendingHolyDays;
return this;
}
setPremiumEndYear(premiumEndYear: number): LiquidationBuilder {
this.premiumEndYear = premiumEndYear;
return this;
}
setPremiumMediumYear(premiumMediumYear: number): LiquidationBuilder {
this.premiumMediumYear = premiumMediumYear;
return this;
}
setLayoffs(layoffs: number): LiquidationBuilder {
this.layoffs = layoffs;
return this;
}
build(): Liquidation {
return new Liquidation(this.basicSalaryMontly, this.monthsWorked, this.pendingHolyDays,
this.premiumEndYear, this.premiumMediumYear, this.layoffs);
}
}
const total = new LiquidationBuilder()
.setBasicSalaryMonth(4500)
.setLayoffs(500)
.setMothsWorked(15)
.setPremiumEndYear(1000)
.setPremiumMediumYear(0)
.setPendingHollyDays(15)
.build();
console.log(total);
console.log(total.Calculate());
Considero que sería bueno para el curso darnos un ejemplo de aplicar este patrón de diseño no tan complejo, algo más corto y ahí si complementar con el ejemplo de los carros que se lleva trabajando en los anteriores patrones de diseño y ahondar un poco más en los requerimientos, con más diagramas y un puntero explicando la historia de usuario, considero que en esta clase se agregó un nivel de complejidad para aplicar el patrón a este ejemplo que quizás se puede ver un poco forzado, sería tener una introducción al patrón Builder más corta y ahí pasar a este ejemplo, algo más sencillo podría ser el proceso de creación de una pizza con Builder como el siguiente:
// Clase Pizza
class Pizza {
private dough: string;
private sauce: string;
private cheese: string;
private toppings: string[];
constructor(
dough: string,
sauce: string,
cheese: string,
toppings: string[]
) {
this.dough = dough;
this.sauce = sauce;
this.cheese = cheese;
this.toppings = toppings;
}
public getDough(): string {
return this.dough;
}
public getSauce(): string {
return this.sauce;
}
public getCheese(): string {
return this.cheese;
}
public getToppings(): string[] {
return this.toppings;
}
}
// Builder
class PizzaBuilder {
private dough: string;
private sauce: string;
private cheese: string;
private toppings: string[];
constructor() {
this.toppings = [];
}
public setDough(dough: string): PizzaBuilder {
this.dough = dough;
return this;
}
public setSauce(sauce: string): PizzaBuilder {
this.sauce = sauce;
return this;
}
public setCheese(cheese: string): PizzaBuilder {
this.cheese = cheese;
return this;
}
public addTopping(topping: string): PizzaBuilder {
this.toppings.push(topping);
return this;
}
public build(): Pizza {
return new Pizza(this.dough, this.sauce, this.cheese, this.toppings);
}
}
// Uso
const pizza = new PizzaBuilder()
.setDough('thin')
.setSauce('tomato')
.setCheese('mozzarella')
.addTopping('mushrooms')
.addTopping('olives')
.addTopping('onions')
.build();
console.log(pizza.getDough()); // thin
console.log(pizza.getSauce()); // tomato
console.log(pizza.getCheese()); // mozzarella
console.log(pizza.getToppings()); // ['mushrooms', 'olives', 'onions']
En el anterior ejemplo, no se dejó una clase Director ya que según refactoring.guru una clase Director no es estrictamente necesaria (aunque recomendada), también recomiendo écharle un vistazo al link para mayor entendimiento de este patrón de diseño.
¡Gracias por la clase!
Otro ejemplo diferente en python
class Car:
def __init__(self):
self.brand = None
self.model = None
self.color = None
self.engine = None
self.transmission = None
def __str__(self):
return f"Coche: {self.brand} {self.model}, Color: {self.color}, Motor: {self.engine}, Transmisión: {self.transmission}"
class CarBuilder:
def __init__(self):
self.car = Car()
def set_brand(self, brand):
self.car.brand = brand
return self
def set_model(self, model):
self.car.model = model
return self
def set_color(self, color):
self.car.color = color
return self
def set_engine(self, engine):
self.car.engine = engine
return self
def set_transmission(self, transmission):
self.car.transmission = transmission
return self
def build(self):
return self.car
class CarDirector:
def __init__(self, builder):
self.builder = builder
def construct_sports_car(self):
return self.builder.set_brand("Ferrari") \
.set_model("488 GTB") \
.set_color("Rojo") \
.set_engine("V8") \
.set_transmission("Automática") \
.build()
def construct_suv(self):
return self.builder.set_brand("BMW") \
.set_model("X5") \
.set_color("Negro") \
.set_engine("V6") \
.set_transmission("Automática") \
.build()
# Uso del Builder y el Director para construir diferentes tipos de coches
builder = CarBuilder()
director = CarDirector(builder)
sports_car = director.construct_sports_car()
suv = director.construct_suv()
print(sports_car)
print(suv)
Se me ocurren dos formas de variar la implementación de la clase Director:
Director.setProductionLine(mastodonSedanProductionLine).constructCvtEdition()
¿Quieres ver más aportes, preguntas y respuestas de la comunidad?