Modèle de conception d'adaptateur en Python

Introduction

La Modèle de conception d'adaptateur est un populaire Modèle de conception structurelle utilisé en génie logiciel. Ce guide explique comment nous pouvons implémenter le modèle de conception d'adaptateur en Python.

Modèles de conception sont des solutions de type modèle - pratiquement des recettes pour résoudre des problèmes récurrents et courants dans le développement de logiciels. Le modèle d'adaptateur est basé sur le concept d'un adaptateur du monde réel ! Par exemple, le chargeur d'un ordinateur portable peut avoir une prise à 3 broches à l'extrémité, mais la prise murale peut n'être qu'une prise à 2 broches. Pour brancher un chargeur à 3 broches dans cette prise, nous aurions besoin d'un adaptateur, qui accepte une prise à 3 broches, et s'adapte l'interface dans le 2 broches prise.

Un chargeur à 2 broches et un chargeur à 3 broches ont la même fonction de base (conduisent l'électricité de la prise à l'ordinateur portable), mais ont une forme différente, et on peut facilement adapter dans l'autre. Chaque fois que vous avez des composants logiciels avec la même fonction de base mais des formes différentes, vous pouvez appliquer le modèle de conception d'adaptateur.

Le modèle d'adaptateur suit ce principe exact. Il permet à deux interfaces incompatibles de fonctionner ensemble sans modifier les composants internes de chaque composant. Ceci est réalisé en adaptant une interface, à une autre, en externe.

Examinons quelques termes de base avant de plonger plus profondément dans le monde des Adapter Patterns :

  • Interface client: Une interface qui spécifie les fonctions que le client doit implémenter.
  • Client: Une classe qui implémente l'interface client.
  • Adapté/Service: La classe incompatible qui doit collaborer avec l'interface client.
  • adaptateur: La classe qui rend possible la collaboration entre le service et le client.

Différents types de modèles d'adaptateur

Le modèle de conception d'adaptateur peut être implémenté de deux manières différentes :

Adaptateur d'objet

Avec cette méthode, la classe adaptateur implémente les méthodes de l'interface client. Ainsi, l'objet client et l'objet adaptateur sont compatibles entre eux. L'objet de service forme un has-a relation avec l'objet adaptateur, c'est-à-dire que l'objet service appartient à l'objet adaptateur.

Nous savons que la classe de service n'est pas compatible avec le client. La classe d'adaptateur s'enroule autour de l'objet de service en s'instanciant avec cet objet. Désormais, l'objet service est accessible via l'objet adaptateur, ce qui permet au client d'interagir avec lui.

Nous pouvons implémenter l'adaptateur d'objet dans tous les langages de programmation modernes.

Adaptateur de classe

Avec cette méthode, l'adaptateur a un is-a relation avec la classe de service. Dans ce scénario, l'adaptateur implémente les méthodes requises par le client, mais il hérite de plusieurs adaptateurs, ce qui lui donne la possibilité d'appeler directement leurs fonctions incompatibles. Le plus gros inconvénient de cette variante est que nous ne pouvons l'utiliser que dans les langages de programmation qui prennent en charge l'héritage multiple de classes.

Implémentation du modèle de conception d'adaptateur en Python

Dans la section ci-dessous, nous allons implémenter le modèle de conception Adapter en Python, en utilisant spécifiquement le variation de l'adaptateur d'objet. La section est divisée en deux parties. Tout d'abord, nous allons créer l'environnement dans lequel le modèle d'adaptateur doit être utilisé. Il est important de voir clairement comment ce modèle peut résoudre certains problèmes logiciels. La deuxième section utilisera un adaptateur pour résoudre le problème.

Problème d'incompatibilité entre les classes

Examinons le problème de compatibilité lorsque le client et la classe de service implémentent des fonctionnalités différentes. Créez une classe client avec les méthodes suivantes et enregistrez-la dans un dossier sous 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")

Ici, nous avons créé un Car classe avec trois méthodes accelerate(), apply_brakes() ainsi que assign_driver(). Nous avons importé le random module et l'a utilisé pour générer des nombres qui définissent la vitesse de la voiture après avoir accéléré et appliqué les freins. Le assign_driver() méthode affiche le nom du conducteur de la voiture.

Ensuite, il faut créer une classe service ou adaptée qui souhaite collaborer avec la classe client Car. Créez une classe de moto comme celle-ci et enregistrez-la dans votre dossier sous 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")  

Une classe de service, Motorcycle est créé ci-dessus avec trois méthodes rev_throttle(), pull_brake_lever()et assign_rider(). Notez la différence entre les méthodes de la classe service et client malgré leurs fonctionnalités similaires. Le accelerator() méthode augmente la vitesse de la voiture tandis que la rev_throttle() méthode augmente la vitesse de la moto. De même, apply_brakes() ainsi que pull_brake_lever() applique les freins dans les véhicules respectifs. Finalement, le assign_driver() ainsi que assign_rider() méthodes assignent le conducteur du véhicule.

Créons ensuite une classe pour accéder à ces différentes méthodes. Tout d'abord, ajoutez un __init.py__ dans le même dossier que vous avez créé car.py ainsi que motorcycle.py:

touch __init__.py

Ajoutez maintenant le code suivant dans un nouveau fichier 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()

Ce script qui crée nos objets client et service. Nous importons d'abord le Car ainsi que Motorcycle classes et créer des objets avec eux. Ensuite, nous invoquons les méthodes de la bike objet (Motorcycle classe). Ensuite, nous invoquons les méthodes de la car objet (Car classe). Une fois exécuté, tout le code mentionné jusqu'à présent fonctionnera.

Cependant, une exception est levée lorsque nous essayons d'invoquer les méthodes du Car classe avec le bike objet. Lorsque nous exécutons ce 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'

Dans ce cas, nous pouvons modifier le Motorcycle classe ou le drive.py script pour utiliser les bonnes méthodes. Cependant, dans de nombreux cas, nous n'avons peut-être pas accès au code source du client ou de la classe de service. En outre, ceci est un exemple simple. Avec des clients et des services plus importants, il peut ne pas être possible de refactoriser l'un ou l'autre au cas où nous briserions la compatibilité avec d'autres systèmes.

Au lieu de cela, nous pouvons utiliser un adaptateur pour combler le fossé de compatibilité entre notre code client et notre objet de service.

Utilisation d'adaptateurs pour résoudre le problème d'incompatibilité

Dans un nouveau fichier, motorcycle_adapter.py, ajoutez la classe suivante :

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)

Nous avons créé un MotorcycleAdapter classe, qui s'instancie avec un objet de service (motorcycle). L'adaptateur implémente les méthodes client qui sont accelerate(), apply_brakes() ainsi que assign_driver(). A l'intérieur du corps du accelerate() méthode, nous avons utilisé la motorcycle instance de l'objet de service pour appeler le rev_throttle() méthode des services. De même, les autres méthodes utilisent les méthodes correspondantes de la Motorcycle classe.

Maintenant, mettons à jour drive.py afin que nous puissions utiliser l'adaptateur dans le try/except bloquer:

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()

Ici,bike_adapter est un objet de la MotorcycleAdapter classe. Nous avons fourni le bike objet à la MotorcycleAdapter constructeur de la classe. L'exécution de ce script nous donne le résultat suivant :

Consultez notre guide pratique et pratique pour apprendre Git, avec les meilleures pratiques, les normes acceptées par l'industrie et la feuille de triche incluse. Arrêtez de googler les commandes Git et en fait apprendre il!

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

Sans avoir à ajuster le sous-jacent Motorcycle classe, nous pouvons le faire fonctionner comme un Car à l'aide d'un adaptateur !

Avantages et inconvénients du modèle de conception d'adaptateur

Les avantages des modèles d'adaptateur sont :

  • Nous pouvons obtenir un faible couplage entre la classe adaptateur et la classe client.
  • Nous pouvons réutiliser la classe d'adaptateur pour incorporer de nombreuses classes de service dans l'application.
  • Nous pouvons augmenter la flexibilité du programme en introduisant plusieurs adaptateurs sans interférer avec le code client

Les inconvénients d'Adapter Pattern sont :

  • La complexité du programme augmente avec l'ajout de la classe d'adaptateur et de la classe de service.
  • Il y a une augmentation des frais généraux dans le programme à mesure que les demandes sont transmises d'une classe à une autre.
  • Adapter Pattern (adaptateur de classe) utilise plusieurs héritages, que tous les langages de programmation peuvent ne pas prendre en charge.

Conclusion

Dans cet article, nous avons découvert le modèle de conception de l'adaptateur, ses types et les problèmes qu'ils résolvent. Nous avons implémenté le modèle d'adaptateur en Python afin que nous puissions interagir avec un Motorcycle objet, comme un Car objet en utilisant un adaptateur afin que l'interface de chaque classe ne change pas.

Horodatage:

Plus de Stackabuse