Patrón de diseño: BUILDER
Hoy vamos a ver un patrón de creación llamado Builder. Es de los más comunes y útiles para crear instancias debido a su fácil implementación y a los beneficios que nos aportan.
Tabla de contenido
Este es un patrón de diseño creacional. El patrón Builder nos ayuda a construir objetos complejos paso por paso.
Imagina que queremos contruir un coche. Si queremos crear un coche pasando argumentos a un inicializador, puede que al final tengamos un inicializador con muchos parámetros y muchos de ellos no se usarían en la mayoría de casos.
Es decir, podemos crear un coche modificando las características que queremos en él: tamaño de las ruedas, número de puertas, material de los asientos, tamaño del motor, color del chasis, tipo de gasolina, etc.
Nuestros tipos de coches pueden variar, pero si ponemos todas estas opciones en un inicializador el número de parámetros sería infinito.
enum CarSize {
case small
case medium
case big
}
class Car {
let numberOfDoors: Int
let color: UIColor
let size: CarSize
init(numberOfDoors: Int, color: UIColor, size: CarSize) {
self.numberOfDoors = numberOfDoors
self.color = color
self.size = size
}
}
Podemos crear una clase base y usarla para heredar de ella cuando queramos modificar propiedades de nuestro coche. Pero a medida que añadamos más parámetros tendremos que incrementar nuestro inicializador. Vamos a ver un ejemplo:
enum EngineSize {
case small
case medium
case big
}
enum GearsType {
case manual
case automatic
}
class SportCar: BaseCar {
let motor: EngineSize
let gears: GearsType
init(numberOfDoors: Int, color: UIColor, size: CarSize, motor: EngineSize, gears: GearsType) {
self.motor = motor
self.gears = gears
super.init(numberOfDoors: numberOfDoors, color: color, size: size)
}
}
Acabamos de crear la clase SportCar que hereda de BaseCar. Hemos creado dos propiedades nuevas para los coches deportivos: motor y gears (marchas). Como ves, a parte de crear los parámetros necesarios para instanciar SportCar, también necesitamos los parámetros necesarios para la clase base (numberOfDoors, color y size). ¿Qué crees que pasará si necesitamos heredar de SportCar para crear un tipo de coche con asientos de piel? Exacto, nuestro inicializador crecerá aún más para esa clase.
Para evitar los problemas que acabamos de ver, puedes pensar que una posible solución es crear un inicializador con todos los parámetros posibles que usas en tus subclases, pero al final acabas teniendo muchos parámetros que no vas a usar y que tienen valores por defecto. Para que te hagas una idea, sería algo así:
class CustomCar {
var numberOfDoors: Int?
var color: UIColor?
var size: CarSize?
var motor: EngineSize?
var gears: GearsType?
init(numberOfDoors: Int?, color: UIColor?, size: CarSize?, motor: EngineSize?, gears: GearsType?) {
self.numberOfDoors = numberOfDoors
self.color = color
self.size = size
self.motor = motor
self.gears = gears
}
convenience init() {
self.init(numberOfDoors: nil, color: nil, size: nil, motor: nil, gears: nil)
}
}
let myCustomCar = CustomCar(numberOfDoors: 4, color: .black, size: .medium, motor: .big, gears: .automatic)
Para evitar estos problemas, podemos usar el patrón Builder para poder crear un objeto paso por paso, en este caso crearemos una clase builder que nos cree un coche. Vamos a ver un ejemplo:
class CarBuilder {
private var car: CustomCar
init() {
self.car = CustomCar()
}
func addDoors(numberOfDoors: Int) -> CarBuilder {
self.car.numberOfDoors = numberOfDoors
return self
}
func addColor(color: UIColor) -> CarBuilder {
self.car.color = color
return self
}
func addSize(size: CarSize) -> CarBuilder {
self.car.size = size
return self
}
func addMotor(size: EngineSize) -> CarBuilder {
self.car.motor = size
return self
}
func build() -> CustomCar {
return self.car
}
}
Hemos creado una clase CarBuilder con una propiedad que es de tipo CustomCar y unos métodos para cambiar las propiedades de nuestro coche. El último método es el más importante ya que nos va a devolver la instancia del CustomCar con todas las propiedas que hayamos modificado.
Al inicializar el builder creamos un CustomCar con todas sus propiedades a nil.
Por lo tanto, si queremos crear un coche con 2 puertas, y con tamaño grande, solo tendríamos que hacer lo siguiente:
let myNewCar = CarBuilder().addDoors(numberOfDoors: 2)
.addSize(size: .big)
.addMotor(size: .big)
.build()
Podemos crear nuestro coche "a la carta" creando métodos en nuestro builder que cambien por debajo la instancia de CustomCar.
Hasta aquí el post de hoy, gracias por leernos! 🤓
Si tienes preguntas no dudes en contactar con nosotros a través de Twitter