Logotipo de Zephyrnet

Patrón de diseño de adaptador en Python

Fecha:

Introducción

El Patrón de diseño de adaptador es un popular Patrón de diseño estructural utilizado en ingeniería de software. Esta guía analiza cómo podemos implementar el patrón de diseño del adaptador en Python.

Patrones de diseño son soluciones similares a plantillas: prácticamente recetas para resolver problemas recurrentes y comunes en el desarrollo de software. ¡El patrón de adaptador se basa en el concepto de un adaptador del mundo real! Por ejemplo, el cargador de una computadora portátil puede tener un enchufe de 3 pines al final, pero el enchufe de la pared puede ser solo un enchufe de 2 pines. Para enchufar un cargador de 3 clavijas en este enchufe, necesitaríamos un adaptador que acepte un enchufe de 3 clavijas y se adapta la interfaz en el 2 pines enchufe.

Un cargador de 2 pines y un cargador de 3 pines tienen la misma función básica (conducir electricidad desde el enchufe hasta la computadora portátil), pero tenga una forma diferente, y uno puede fácilmente adaptar en el otro. Siempre que tenga componentes de software con la misma función básica pero formas diferentes, puede aplicar el patrón de diseño del adaptador.

El patrón de adaptador sigue exactamente este principio. Permite que dos interfaces incompatibles funcionen juntas sin modificar las partes internas de cada componente. Esto se logra adaptando una interfaz, a otra, externamente.

Veamos un poco de terminología básica antes de profundizar en el mundo de los patrones de adaptador:

  • Interfaz de cliente: Una interfaz que especifica las funciones que el cliente debe implementar.
  • Cliente: una clase que implementa la interfaz del cliente.
  • Adaptado/Servicio: la clase incompatible que necesita colaborar con la interfaz del cliente.
  • adaptador: La clase que hace posible la colaboración entre el servicio y el cliente.

Diferentes tipos de patrones de adaptador

El patrón de diseño del adaptador se puede implementar de dos maneras diferentes:

Adaptador de objetos

Con este método, la clase de adaptador implementa los métodos desde la interfaz del cliente. Por tanto, el objeto cliente y el objeto adaptador son compatibles entre sí. El objeto de servicio forma un has-a relación con el objeto adaptador, es decir, el objeto de servicio pertenece al objeto adaptador.

Sabemos que la clase de servicio no es compatible con el cliente. La clase de adaptador envuelve el objeto de servicio al instanciarse con ese objeto. Ahora, se puede acceder al objeto de servicio a través del objeto de adaptador, lo que permite que el cliente interactúe con él.

Podemos implementar el adaptador de objetos en todos los lenguajes de programación modernos.

Adaptador de clase

Con este método, el adaptador tiene una is-a relación con la clase de servicio. En este escenario, el adaptador implementa los métodos requeridos por el cliente, pero hereda de varios adaptadores, lo que le permite llamar directamente a sus funciones incompatibles. El mayor inconveniente de esta variación es que solo podemos usarla en los lenguajes de programación que admiten la herencia múltiple de clases.

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

En la siguiente sección, implementaremos el patrón de diseño del Adaptador en Python, específicamente usando el variación del adaptador de objetos. La sección se divide en dos partes. Primero, crearemos el entorno donde se debe usar el patrón de adaptador. Es importante ver claramente cómo este patrón puede resolver algunos problemas de software. La segunda sección utilizará un adaptador para resolver el problema.

Problema de incompatibilidad entre clases

Veamos el problema de compatibilidad cuando el cliente y la clase de servicio implementan diferentes funcionalidades. Cree una clase de cliente con los siguientes métodos y guárdela en una carpeta como car.py:

import random

class Car:
    def __init__(self):
        self.generator = random.Random()

    def accelerate(self):
        random_num = self.generator.randint(50, 100)
        speed = random_num
        print(f"The speed of the car is {speed} mph")

    def apply_brakes(self):
        random_num = self.generator.randint(20, 40)
        speed = random_num
        print(f"The speed of the car is {speed} mph after applying the brakes")

    def assign_driver(self, driver_name):
        print(f"{driver_name} is driving the car")

Aquí, hemos creado un Car clase con tres métodos accelerate(), apply_brakes() y assign_driver(). Importamos el random módulo y lo usó para generar números que establecen la velocidad del automóvil después de acelerar y aplicar los frenos. los assign_driver() método muestra el nombre del conductor del coche.

A continuación, tenemos que crear una clase de servicio o adaptada que desee colaborar con la clase de cliente. Car. Cree una clase de motocicleta como esta y guárdela en su carpeta como motorcycle.py:

import random

class Motorcycle:
    def __init__(self):
        self.generator = random.Random()

    def rev_throttle(self):
        random_num = self.generator.randint(50, 100)
        speed = random_num
        print(f"The speed of the motorcycle is {speed} mph")

    def pull_brake_lever(self):
        random_num = self.generator.randint(20, 40)
        speed = random_num
        print(
            f"The speed of the motorcycle is {speed} mph after applying the brakes")

    def assign_rider(self, rider_name):
        print(f"{rider_name} is riding the motorcycle")  

Una clase de servicio, Motorcycle se crea arriba con tres métodos rev_throttle(), pull_brake_lever()y assign_rider(). Observe la diferencia entre los métodos del servicio y la clase de cliente a pesar de su funcionalidad similar. los accelerator() método aumenta la velocidad del coche mientras que el rev_throttle() método aumenta la velocidad de la motocicleta. Igualmente, apply_brakes() y pull_brake_lever() aplica los frenos en los vehículos respectivos. Finalmente, el assign_driver() y assign_rider() métodos asignan al operador del vehículo.

A continuación, creemos una clase para acceder a estos diferentes métodos. Primero, agregue un __init.py__ en la misma carpeta que creaste car.py y motorcycle.py:

touch __init__.py

Ahora agregue el siguiente código en un nuevo archivo drive.py:

from car import Car
from motorcycle import Motorcycle
import traceback

if __name__ == '__main__':
    car = Car()
    bike = Motorcycle()

    print("The Motorcyclen")
    bike.assign_rider("Subodh")
    bike.rev_throttle()
    bike.pull_brake_lever()
    print("n")

    print("The Carn")
    car.assign_driver("Sushant")
    car.accelerate()
    car.apply_brakes()
    print("n")

    print("Attempting to call client methods with the service objectn")

    try:
        bike.assign_driver("Robert")
        bike.accelerate()
        bike.apply_brakes()
    except AttributeError:
        print("Oops! bike object cannot access car methods")
        traceback.print_exc()

Este script que crea nuestros objetos de cliente y servicio. Primero importamos el Car y Motorcycle clases y crear objetos con ellas. Luego invocamos los métodos del bike objetoMotorcycle clase). Después, invocamos los métodos del car objetoCar clase). Cuando se ejecuta, todo el código mencionado hasta ahora funcionará.

Sin embargo, se produce una excepción cuando intentamos invocar los métodos del Car clase con el bike objeto. Cuando ejecutamos este script:

The Motorcycle

Subodh is riding the motorcycle
The speed of the motorcycle is 91 mph
The speed of the motorcycle is 37 mph after applying the brakes


The Car

Sushant is driving the car
The speed of the car is 59 mph
The speed of the car is 33 mph after applying the brakes


Attempting to call client methods with the service object

Oops! bike object cannot access car methods
Traceback (most recent call last):
  File "drive.py", line 24, in 
    bike.assign_driver("Robert")
AttributeError: 'Motorcycle' object has no attribute 'assign_driver'

En este caso, podemos modificar el Motorcycle clase o el drive.py script para usar los métodos correctos. Sin embargo, en muchos casos, es posible que no tengamos acceso al código fuente del cliente o clase de servicio. Además, este es un ejemplo simple. Con clientes y servicios más grandes, puede que no sea factible refactorizar ninguno de ellos en caso de que rompamos la compatibilidad con otros sistemas.

En su lugar, podemos usar un adaptador para cerrar la brecha de compatibilidad entre nuestro código de cliente y nuestro objeto de servicio.

Uso de adaptadores para resolver el problema de incompatibilidad

En un nuevo archivo, motorcycle_adapter.py, agregue la siguiente clase:

class MotorcycleAdapter:

    def __init__(self, motorcycle):
        self.motorcycle = motorcycle

    def accelerate(self):
        self.motorcycle.rev_throttle()

    def apply_brakes(self):
        self.motorcycle.pull_brake_lever()

    def assign_driver(self, name):
        self.motorcycle.assign_rider(name)

Hemos creado un MotorcycleAdapter clase, que se instancia a sí misma con un objeto de servicio (motorcycle). El adaptador implementa los métodos de cliente que son accelerate(), apply_brakes() y assign_driver(). Dentro del cuerpo del accelerate() método, hemos utilizado el motorcycle instancia del objeto de servicio para llamar al rev_throttle() método de servicio. Asimismo, los otros métodos utilizan los métodos correspondientes del Motorcycle clase.

Ahora, actualicemos drive.py para que podamos usar el adaptador en el try/except bloque:

from car import Car
from motorcycle import Motorcycle
from motorcycle_adapter import MotorcycleAdapter 
import traceback

if __name__ == '__main__':
    car = Car()
    bike = Motorcycle()
    bike_adapter = MotorcycleAdapter(bike) 

    ...

    try:
        print("Attempting to call client methods with the service object using an adaptern")
        bike_adapter.assign_driver("Robert")
        bike_adapter.accelerate()
        bike_adapter.apply_brakes()
    except AttributeError:
        print("Oops! bike object cannot access car methods")
        traceback.print_exc()

Aquí,bike_adapter es un objeto de la MotorcycleAdapter clase. Suministramos el bike con la MotorcycleAdapter constructor de clase. Ejecutar este script nos da el siguiente resultado:

Consulte nuestra guía práctica y práctica para aprender Git, con las mejores prácticas, los estándares aceptados por la industria y la hoja de trucos incluida. Deja de buscar en Google los comandos de Git y, de hecho, aprenden ella!

The Motorcycle

Subodh is riding the motorcycle
The speed of the motorcycle is 88 mph
The speed of the motorcycle is 35 mph after applying the brakes


The Car

Sushant is driving the car
The speed of the car is 91 mph
The speed of the car is 24 mph after applying the brakes


Attempting to call client methods with the service object

Attempting to call client methods with the service object using an adapter

Robert is riding the motorcyle
The speed of the motorcycle is 67 mph
The speed of the motorcycle is 25 mph after applying the brakes

Sin tener que ajustar el subyacente Motorcycle clase, podemos hacer que funcione como un Car usando un adaptador!

Pros y contras del patrón de diseño del adaptador

Las ventajas de los patrones de adaptador son:

  • Podemos lograr un acoplamiento bajo entre la clase de adaptador y la clase de cliente.
  • Podemos reutilizar la clase de adaptador para incorporar numerosas clases de servicio en la aplicación.
  • Podemos aumentar la flexibilidad del programa introduciendo múltiples adaptadores sin interferir con el código del cliente

Las desventajas del patrón adaptador son:

  • La complejidad del programa aumenta con la adición de la clase de adaptador y la clase de servicio.
  • Hay un aumento de los gastos generales en el programa a medida que las solicitudes se envían de una clase a otra.
  • El patrón de adaptador (adaptador de clase) utiliza herencias múltiples, que pueden no ser compatibles con todos los lenguajes de programación.

Conclusión

En este artículo, aprendimos sobre el patrón de diseño del adaptador, sus tipos y los problemas que resuelven. Implementamos el patrón de adaptador en Python para que podamos interactuar con un Motorcycle objeto, como un Car objeto usando un adaptador para que la interfaz de cada clase no cambie.

punto_img

Información más reciente

punto_img