El patrón de diseño del puente con Python

En este tutorial, exploraremos la motivación detrás del patrón de diseño de puente y lo implementaremos en Python.

Introducción

The Bridge Design Pattern es un Patrón de diseño estructural, que separa la abstracción de la implementación. En este artículo, cubriremos la motivación y la implementación del patrón de diseño de puentes en Python.

Patrones de diseño se refiere a un conjunto de prácticas o soluciones estandarizadas para problemas arquitectónicos comunes en la ingeniería de software.

Motivación detrás del patrón de diseño del puente {#motivación detrás del patrón de diseño del puente}

El Patrón Puente previene lo que se llama la explosión de complejidad del producto cartesiano.

El problema será obvio pasando por un ejemplo. Suponga que está implementando un Avión. Puede ser un avión militar o comercial. Además, puede ser un avión de pasajeros/soldado o de carga.

Un enfoque para implementar esto es tener aviones MilitaryPassenger, MilitaryCargo, CommercialPassenger y CommercialCargo.

Aquí la complejidad del producto cartesiano es 2 x 2 = 4. Este número no es innovador a esta escala, pero cuando incluye más clases y variaciones, puede aumentar exponencialmente y muy rápidamente se volverá inmanejable.

El Patrón Puente se utiliza, además, como un puente entre clases (implementación Avión) y sus características (si es un avión de pasajeros o de carga). Favorece la composición sobre herencia.

Usando el patrón, crea una clase para cada uno de los tipos en diferentes categorías. Por ejemplo, en nuestro ejemplo, tendríamos un CommercialPlane y MilitaryPlane como entidades, con CargoCarrier y PassengerCarrier como entidades separadas.

Puede que no parezca que hemos hecho mucho ya que todavía tenemos 4 clases, pero imagina esto en una escala. Puede tener clases nPlane y solo tener CargoCarrier y PassengerCarrier que puede aplicar a estos aviones.

Una forma aún mejor sería tener clases principales - Carrier y Plane. Para la clase principal Carrier, puede crear dos clases secundarias: Cargo y Passenger. Del mismo modo, para la clase principal ‘Avión’, hay dos clases secundarias: ‘Militar’ y ‘Comercial’.

A continuación, necesitará una forma de conectarse, o en otras palabras, unir las subclases Carrier y Plane. Puede hacerlo pasando una de estas dos clases como un valor de parámetro en el constructor de la otra clase. Al implementar el patrón, podremos combinar cualquiera de las subclases.

Finalmente, veamos cómo podemos implementar el patrón de diseño de puente en Python.

Implementación del patrón de diseño de puente en Python

Como dije antes, crearemos una clase principal Carrier con dos métodos abstractos: carry_military() y carry_passenger(). A continuación, podemos crear una clase secundaria Cargo que hereda de la clase Carrier e implementa los métodos carry_military() y carry_commercial().

Para evitar la pendiente resbaladiza de agregar variaciones de clases, definiremos un Transportista con dos métodos abstractos: carry_military() y cary_passenger().

La clase Carrier tendrá además dos clases secundarias: Cargo y Passenger, que heredan sus métodos abstractos y los implementan:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
# Passenger & Cargo Carriers

class Carrier:
    def carry_military(self, items):
        pass
    
    def carry_commercial(self, items):
        pass
    
class Cargo(Carrier):
    def carry_military(self, items):
        print("The plane carries ", items," military cargo goods")
        
    def carry_commercial(self, items):
        print("The plane carries ", items," commercial cargo goods") 

class Passenger(Carrier):
    def carry_military(self, passengers):
        print("The plane carries ",  passengers , " military passengers")
        
    def carry_commercial(self, passengers):
        print("The plane carries ",  passengers , " commercial passengers") 

Del mismo modo, crearemos una clase Plane con dos métodos abstractos: display_description() y add_objects(), así como dos clases secundarias: Commercial y Military. Pasaremos un Carrier al constructor de la clase Plane. Este es el puente.

Si el avión es un avión Comercial, es Cargo y Passenger devolverá carry_commercial(), y viceversa.

El número de pasajeros/mercancías se almacenará en la variable self.objects que se pasa como parámetro al método carry_commercial():

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# Military & Commercial Planes
class Plane:
    def __init__(self, Carrier):
        self.carrier = Carrier
        
    def display_description(self):
        pass
    
    def add_objects(self):
        pass
    
class Commercial(Plane):
    def __init__(self, Carrier, objects):
        super().__init__(Carrier)
        self.objects = objects

    def display_description(self):
        self.carrier.carry_commercial(self.objects)

    def add_objects(self, new_objects):
        self.objects += new_objects   

class Military(Plane):
    def __init__(self, Carrier, objects):
        super().__init__(Carrier)
        self.objects = objects

    def display_description(self):
        self.carrier.carry_military(self.objects)

    def add_objects(self, new_objects):
        self.objects += new_objects

Nuestras clases están preparadas y listas. Ahora es el momento de crear algunos objetos y hacer que puenten entre ellos a través de la llamada al constructor mencionada anteriormente.

Veamos un ejemplo:

1
2
3
4
5
6
7
8
cargo = Cargo()
passenger = Passenger()

# Bridging Military and Cargo classes
military1 = Military(cargo , 100)
military1.display_description()
military1.add_objects(25)
military1.display_description()

Aquí, hemos instanciado objetos para las clases Cargo y Passenger. Luego, en una llamada de constructor a la clase Military, hemos pasado la instancia cargo. Dado que es un avión militar, la carga se considera carga militar.

Por lo tanto, el método display_description() imprimirá detalles sobre la carga militar. Además, hemos agregado otros 25 objetos además de esta carga:

1
2
The plane carries 100 military cargo goods
The plane carries 125 military cargo goods

Del mismo modo, también podemos conectar las clases ‘Militar’ y ‘Pasajero’:

1
2
3
4
5
6
7
8
cargo = Cargo()
passenger = Passenger()

# Bridging Military and Passenger classes
military2 = Military(passenger , 250)
military2.display_description()
military2.add_objects(10)
military2.display_description()

Naturalmente, el método display_description() da como resultado el número de pasajeros militares que transportamos:

1
2
The plane carries 250 military passengers
The plane carries 260 military passengers

Del mismo modo, podemos unir Commercial y Passenger:

1
2
3
4
5
# Bridging Commercial and Passenger
commercial1 = Commercial(passenger , 400)
commercial1.display_description()
commercial1.add_objects(50)
commercial1.display_description()

Lo que resultará en:

1
2
The plane carries 400 commercial passengers
The plane carries 450 commercial passengers

Y finalmente, podemos conectar las clases Comercial y Cargo:

1
2
3
4
5
# Bridging Commercial and Cargo
commercial2 = Commercial(cargo, 150)
commercial2.display_description()
commercial2.add_objects(15)
commercial2.display_description()

Lo que resulta en:

1
2
The plane carries 150 commercial cargo goods
The plane carries 165 commercial cargo goods

Conclusión

El Patrón de Diseño de Puente es un Patrón de Diseño Estructural, que separa la abstracción de la implementación. En este artículo, hemos explorado la motivación detrás del Patrón de diseño de puente y cómo funciona.

Luego, implementamos el patrón en Python, mostrando cómo funciona el patrón.