Diario de Python | #16. Patrón Builder
Tags: devjournal, python, spanish
Published: 2023-05-01 16:40:57

Estaba mirando un video de Youtube sobre un desafío de programación en Python, y en un momento mencionan algo como "esto se podría implementar con el patrón Builder".

Esto me remontó a mis tiempos en Java, allá por el año [inserte aquí fecha que no recuerdo]. En esa época tu seniority se definía por cuántos patrones de diseño conocías, y cómo podías implementarlos. Fueron buenos años, porque cuando entendías cada uno, te dabas cuenta de que ya no podías vivir sin ellos.

## El patrón Builder, o cómo construir cosas.
Este patrón de diseño fue pensado para trabajar sobre clases que se componían de muchísimos atributos, y no siempre queríamos inicializarlos todos.

Por lo cual, tomando la base de los famosos métodos setters, se creó el patrón Builder, el cual permitía "construir parcialmente" a un objeto mediante métodos que apuntaban a propiedades de formas independientes. Era básicamente un conjunto sofisticado de setters.

## La implementación
Si quieren saltar al ejemplo terminado, les dejo el [archivo](https://github.com/maxwellnewage/python-practices/blob/ec996f5c07a88848f2a4c3b4c0058192e3d561cf/level_advance/design_pattern_builder.py) de mi repositorio de prácticas en Python. Ahora pasemos a la implementación paso a paso.

Empecé generando una clase _Hero_, la cual se compone de los típicos atributos de un juego RPG: nombre, ataque, defensa, dinero y vida.

```python
class Hero:
    def __init__(self, name):
        self.name = name
        self.hp = 100
        self.money = 10
        self.attack = 0
        self.defense = 0

    def __str__(self):
        return f"Hero: {self.name} | 💰{self.money} ⚔{self.attack} 🛡{self.defense}"
```

Luego construí una clase _HeroBuilder_, la cual dentro de su constructor tiene una instancia vacía de _Hero_, pero debe pasar obligatoriamente el parámetro _name_ por obvias razones.

```python
class HeroBuilder:
    def __init__(self, name):
        self.hero = Hero(name)
```

Acá es donde la cosa se pone interesante: para definir el ataque de nuestro heroe, vamos a crear un setter dentro del mismo builder:

```python
def set_attack(self, attack):
    self.hero.attack = max(attack, 0)
    return self
```

Defino el attack del hero llamando a _self.hero_ porque es el objeto que inicializé en mi constructor. Pero retorno _self_, lo cual puede resultar confuso para muchos.

La idea es retornar el objeto mismo, entonces esto nos permite seguir trabajando en la instancia. Muchos me pueden decir: pero podría hacer lo mismo con un objeto _hero_ y sus respectivos setters. Algo como esto:

```python
hero = Hero("Tomas")
hero.set_attack(10)
hero.set_defense(5)
```

Y es cierto, pero no podrías encadenarlos así:

```python
hero.set_attack(10).set_defense(5)
```

Y por esto necesitamos retornar el _self_, que en nuestro caso nos devolvería el _hero_ mismo. Pero continuemos con el ejemplo:

```python
def add_money(self, money):
    self.hero.money += max(money, 0)
    return self
```

Este método es un setter con incremento, porque podemos llamarlo y aumentar el dinero con cada llamada (siempre y cuando sea un valor mayor a cero).

```python
def get_hero(self):
    return self.hero
```

Y finalmente este método nos retornará el objeto _hero_, el cual con nuestro builder estuvimos modificando. La pregunta es: ¿cómo implementamos esto?

```python
builder = HeroBuilder("Tomas")
builder.set_attack(5).set_defense(10)
builder.add_money(10).add_money(20).add_money(30)
hero = builder.get_hero()

print(hero)
```

En primer lugar armamos nuestro objeto builder. Luego llamamos tantos métodos como queramos. Pueden notar que llamo a _add_money_ unas tres veces, lo cual demuestra la flexibilidad de este patrón. Por otro lado, noten como encadeno los métodos: seguramente vieron este tipo de implementaciones en muchas librerías que han utilizado. El patrón Builder está en todas partes, y es uno de los más poderosos que existen a la hora de personalizar objetos.

Finalmente pueden observar como declaro mi objeto _hero_ y luego lo imprimo en pantalla. Esto da como resultado lo siguiente:

```
Hero: Tomas | 💰70 ⚔5 🛡10
```

## Conclusiones
Me resulta muy entretenido jugar con los objetos y darles mi forma. Este patrón de diseño me lo permite sin ninguna limitación, porque como dije anteriormente, es un modo eficiente de trabajar con setters. 

Espero que hayan disfrutado esta entrega, ¡no olviden darle like y compartirlo para alardear de que ahora conocen otro interesante patrón de diseño!