Adapterentwurfsmuster in Python

Einleitung

Das Designmuster-Adapter ist ein beliebter Strukturelles Designmuster in der Softwareentwicklung verwendet. In diesem Leitfaden wird gezeigt, wie wir das Adapter-Entwurfsmuster in Python implementieren können.

Designmuster sind vorlagenartige Lösungen – quasi Rezepte zur Lösung wiederkehrender, häufiger Probleme in der Softwareentwicklung. Das Adaptermuster basiert auf dem Konzept eines realen Adapters! Beispielsweise kann das Ladegerät eines Laptops am Ende einen 3-poligen Stecker haben, aber die Steckdose kann nur eine 2-polige Buchse sein. Um ein 3-poliges Ladegerät an diese Buchse anzuschließen, benötigen wir einen Adapter, der einen 3-poligen Stecker akzeptiert, und passt sich an die Schnittstelle in die 2-pin Steckdose.

Ein 2-Pin-Ladegerät und ein 3-Pin-Ladegerät haben die gleiche Grundfunktion (Strom von der Steckdose zum Laptop leiten), aber haben eine andere Form, und man kann leicht automatisch in die andere. Immer dann, wenn Sie Softwarekomponenten mit gleicher Grundfunktion, aber unterschiedlichen Formen haben, können Sie das Adapter Design Pattern anwenden.

Das Adapter Pattern folgt genau diesem Prinzip. Es ermöglicht die Zusammenarbeit zweier inkompatibler Schnittstellen, ohne die Interna jeder Komponente zu ändern. Dies wird erreicht, indem eine Schnittstelle extern an eine andere angepasst wird.

Schauen wir uns einige grundlegende Begriffe an, bevor wir tiefer in die Welt der Adaptermuster eintauchen:

  • Client-Schnittstelle: Eine Schnittstelle, die die Funktionen angibt, die der Client implementieren soll.
  • Kunden: Eine Klasse, die die Client-Schnittstelle implementiert.
  • Angepasster/Dienst: Die inkompatible Klasse, die mit der Clientschnittstelle zusammenarbeiten muss.
  • Aufsatz: Die Klasse, die die Zusammenarbeit zwischen dem Dienst und dem Client ermöglicht.

Verschiedene Arten von Adaptermustern

Das Adapterentwurfsmuster kann auf zwei verschiedene Arten implementiert werden:

Objektadapter

Bei dieser Methode implementiert die Adapterklasse die Methoden der Client-Schnittstelle. Somit sind das Client-Objekt und das Adapter-Objekt miteinander kompatibel. Das Dienstobjekt bildet a has-a Beziehung zum Adapterobjekt, dh das Dienstobjekt gehört zum Adapterobjekt.

Wir wissen, dass die Serviceklasse nicht mit dem Client kompatibel ist. Die Adapterklasse umschließt das Dienstobjekt, indem sie sich selbst mit diesem Objekt instanziiert. Jetzt kann über das Adapterobjekt auf das Dienstobjekt zugegriffen werden, sodass der Client damit interagieren kann.

Wir können den Objektadapter in allen modernen Programmiersprachen implementieren.

Klassenadapter

Bei dieser Methode hat der Adapter eine is-a Beziehung zur Dienstklasse. In diesem Szenario implementiert der Adapter die vom Client benötigten Methoden, erbt jedoch von mehreren Adaptees, sodass er deren inkompatible Funktionen direkt aufrufen kann. Der größte Nachteil bei dieser Variante ist, dass wir sie nur in den Programmiersprachen verwenden können, die die Mehrfachvererbung von Klassen unterstützen.

Implementierung des Adapter-Entwurfsmusters in Python

Im folgenden Abschnitt implementieren wir das Adapter-Entwurfsmuster in Python, insbesondere unter Verwendung von Objekt-Adapter-Variation. Der Abschnitt ist in zwei Teile gegliedert. Zuerst erstellen wir die Umgebung, in der das Adaptermuster verwendet werden soll. Es ist wichtig, klar zu sehen, wie dieses Muster einige Softwareprobleme lösen kann. Im zweiten Abschnitt wird ein Adapter verwendet, um das Problem zu beheben.

Inkompatibilitätsproblem zwischen Klassen

Betrachten wir das Kompatibilitätsproblem, wenn die Client- und Dienstklasse unterschiedliche Funktionalitäten implementieren. Erstellen Sie eine Client-Klasse mit den folgenden Methoden und speichern Sie sie in einem Ordner unter 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")

Hier haben wir eine erstellt Car Klasse mit drei Methoden accelerate(), apply_brakes() und assign_driver(). Wir haben die importiert random Modul und generierte daraus Zahlen, die die Geschwindigkeit des Autos nach dem Beschleunigen und Bremsen festlegen. Das assign_driver() Methode zeigt den Namen des Autofahrers an.

Als nächstes müssen wir eine Service- oder Adaptee-Klasse erstellen, die mit der Client-Klasse zusammenarbeiten möchte Car. Erstellen Sie eine Motorradklasse wie diese und speichern Sie sie in Ihrem Ordner unter 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")  

Eine Dienstklasse, Motorcycle wird oben mit drei Methoden erstellt rev_throttle(), pull_brake_lever() und assign_rider(). Beachten Sie den Unterschied zwischen den Methoden der Service- und der Client-Klasse trotz ihrer ähnlichen Funktionalität. Das accelerator() Methode erhöht die Geschwindigkeit des Autos, während die rev_throttle() Methode erhöht die Geschwindigkeit des Motorrads. Ebenfalls, apply_brakes() und pull_brake_lever() betätigt die Bremsen in den jeweiligen Fahrzeugen. Endlich, das assign_driver() und assign_rider() Methoden weisen den Fahrzeugbetreiber zu.

Als Nächstes erstellen wir eine Klasse für den Zugriff auf diese verschiedenen Methoden. Fügen Sie zuerst eine hinzu __init.py__ in demselben Ordner, den Sie erstellt haben car.py und motorcycle.py:

touch __init__.py

Fügen Sie nun den folgenden Code in einer neuen Datei hinzu 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()

Dieses Skript, das unsere Client- und Dienstobjekte erstellt. Wir importieren zuerst die Car und Motorcycle Klassen und erstellen Sie Objekte mit ihnen. Dann rufen wir die Methoden aus der auf bike Objekt (Motorcycle Klasse). Danach rufen wir die Methoden von auf car Objekt (Car Klasse). Bei der Ausführung funktioniert der gesamte bisher erwähnte Code.

Es wird jedoch eine Ausnahme ausgelöst, wenn wir versuchen, die Methoden von aufzurufen Car klasse mit dem bike Objekt. Wenn wir dieses Skript ausführen:

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'

In diesem Fall können wir die ändern Motorcycle Klasse oder die drive.py Skript, um die richtigen Methoden zu verwenden. In vielen Fällen haben wir jedoch möglicherweise keinen Zugriff auf den Quellcode der Client- oder Dienstklasse. Auch dies ist ein einfaches Beispiel. Bei größeren Clients und Diensten ist es möglicherweise nicht möglich, einen von ihnen umzugestalten, falls wir die Kompatibilität mit anderen Systemen brechen.

Stattdessen können wir einen Adapter verwenden, um die Kompatibilitätslücke zwischen unserem Clientcode und unserem Dienstobjekt zu schließen.

Verwenden von Adaptern zum Lösen des Inkompatibilitätsproblems

In einer neuen Datei motorcycle_adapter.py, fügen Sie die folgende Klasse hinzu:

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)

Wir haben ein MotorcycleAdapter Klasse, die sich selbst mit einem Dienstobjekt (motorcycle). Der Adapter implementiert die Client-Methoden, die sind accelerate(), apply_brakes() und assign_driver(). Im Inneren des Körpers der accelerate() Methode haben wir die verwendet motorcycle Instanz des Dienstobjekts zum Aufrufen der rev_throttle() Service-Methode. Ebenso verwenden die anderen Methoden die entsprechenden Methoden der Motorcycle Klasse.

Lassen Sie uns jetzt aktualisieren drive.py damit wir den Adapter in der verwenden können try/except Block:

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

Hierbike_adapter ist ein Objekt der MotorcycleAdapter Klasse. Wir haben die geliefert bike Objekt an die MotorcycleAdapter Konstruktor der Klasse. Die Ausführung dieses Skripts gibt uns die folgende Ausgabe:

Sehen Sie sich unseren praxisnahen, praktischen Leitfaden zum Erlernen von Git an, mit Best Practices, branchenweit akzeptierten Standards und einem mitgelieferten Spickzettel. Hören Sie auf, Git-Befehle zu googeln und tatsächlich in Verbindung, um es!

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

Ohne den Basiswert anpassen zu müssen Motorcycle Klasse, wir können es zum Laufen bringen wie a Car Verwendung eines Adapters!

Vor- und Nachteile des Adapterdesignmusters

Die Vorteile von Adaptermustern sind:

  • Wir können eine geringe Kopplung zwischen der Adapterklasse und der Clientklasse erreichen.
  • Wir können die Adapterklasse wiederverwenden, um zahlreiche Dienstklassen in die Anwendung einzubinden.
  • Wir können die Flexibilität des Programms erhöhen, indem wir mehrere Adapter einführen, ohne den Client-Code zu beeinträchtigen

Die Nachteile von Adapter Pattern sind:

  • Die Komplexität des Programms steigt mit dem Hinzufügen von Adapterklasse und Dienstklasse.
  • Da die Anfragen von einer Klasse zur anderen weitergeleitet werden, erhöht sich der Overhead im Programm.
  • Adaptermuster (Klassenadapter) verwendet mehrere Vererbungen, die möglicherweise nicht von allen Programmiersprachen unterstützt werden.

Zusammenfassung

In diesem Artikel haben wir etwas über das Adapterdesignmuster, seine Typen und die Probleme, die sie lösen, erfahren. Wir haben das Adaptermuster in Python implementiert, damit wir mit a interagieren können Motorcycle Objekt, wie ein Car Objekt, indem Sie einen Adapter verwenden, damit sich die Schnittstelle jeder Klasse nicht ändert.

Zeitstempel:

Mehr von Stapelmissbrauch