Adapterontwerppatroon in Python

Introductie

De Adapter ontwerppatroon is populair Structureel ontwerppatroon gebruikt in software-engineering. Deze handleiding bekijkt hoe we het Adapter Design Pattern in Python kunnen implementeren.

Ontwerp patronen zijn sjabloonachtige oplossingen - praktische recepten voor het oplossen van terugkerende, veelvoorkomende problemen bij softwareontwikkeling. Het adapterpatroon is gebaseerd op het concept van een echte adapter! Zo kan de oplader van een laptop aan het uiteinde een 3-polige stekker hebben, maar mag het stopcontact alleen een 2-polige aansluiting zijn. Om een โ€‹โ€‹3-pins oplader in dit stopcontact te steken, hebben we een adapter nodig die een 3-pins stekker accepteert, en aanpast de interface in de 2-pin stopcontact.

Een 2-pins oplader en een 3-pins oplader hebben dezelfde basisfunctie (stroom van het stopcontact naar de laptop geleiden), maar hebben een andere vorm, en men kan gemakkelijk aanpassen in de andere. Wanneer u softwarecomponenten heeft met dezelfde basisfunctie maar verschillende vormen, kunt u het Adapter Design Pattern toepassen.

Het adapterpatroon volgt exact dit principe. Hiermee kunnen twee incompatibele interfaces samenwerken zonder de binnenkant van elk onderdeel te wijzigen. Dit wordt bereikt door de ene interface extern aan de andere aan te passen.

Laten we wat basisterminologie bekijken voordat we dieper in de wereld van adapterpatronen duiken:

  • Client-interface: Een interface die de functies specificeert die de client moet implementeren.
  • CLIร‹NT: Een klasse die de clientinterface implementeert.
  • Aangepast/Service: De incompatibele klasse die moet samenwerken met de clientinterface.
  • Adapter: De klasse die de samenwerking tussen de dienst en de klant mogelijk maakt.

Verschillende soorten adapterpatronen

Het ontwerppatroon van de adapter kan op twee verschillende manieren worden geรฏmplementeerd:

Object-adapter

Met deze methode implementeert de adapterklasse de methoden van de clientinterface. Het client-object en het adapter-object zijn dus compatibel met elkaar. Het serviceobject vormt a has-a relatie met het adapterobject dwz het serviceobject hoort bij het adapterobject.

We weten dat de serviceklasse niet compatibel is met de klant. De adapterklasse wikkelt zich rond het serviceobject door zichzelf te instantiรซren met dat object. Nu is het serviceobject toegankelijk via het adapterobject, zodat de client ermee kan communiceren.

We kunnen de objectadapter in alle moderne programmeertalen implementeren.

Klasse Adapter

Bij deze methode heeft de adapter een is-a relatie met de serviceklasse. In dit scenario implementeert de adapter de methoden die door de client worden vereist, maar hij erft van meerdere adaptees, waardoor hij de mogelijkheid heeft om hun incompatibele functies rechtstreeks aan te roepen. Het grootste nadeel van deze variant is dat we deze alleen kunnen gebruiken in de programmeertalen die meerdere overerving van klassen ondersteunen.

Implementatie van het adapterontwerppatroon in Python

In de onderstaande sectie zullen we het Adapter-ontwerppatroon in Python implementeren, met name met behulp van de variatie objectadapter. De sectie is opgedeeld in twee delen. Eerst zullen we de omgeving creรซren waar het Adapter Pattern moet worden gebruikt. Het is belangrijk om duidelijk te zien hoe dit patroon sommige softwareproblemen kan oplossen. In het tweede gedeelte wordt een adapter gebruikt om het probleem op te lossen.

Incompatibiliteitsprobleem tussen klassen

Laten we eens kijken naar het compatibiliteitsprobleem wanneer de client en de serviceklasse verschillende functionaliteiten implementeren. Maak een clientklasse met de volgende methoden en sla deze op in een map als 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 hebben we een gemaakt Car klasse met drie methoden accelerate(), apply_brakes() en assign_driver(). We importeerden de random module en gebruikte deze om getallen te genereren die de snelheid van de auto bepalen na accelereren en remmen. De assign_driver() methode geeft de naam van de automobilist weer.

Vervolgens moeten we een service- of adaptee-klasse maken die wil samenwerken met de klantklasse Car. Maak op deze manier een motorklasse aan en sla deze op in je map als 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")  

Een serviceklasse, Motorcycle is hierboven gemaakt met drie methoden rev_throttle(), pull_brake_lever() en assign_rider(). Let op het verschil tussen de methoden van de service en de clientklasse, ondanks hun vergelijkbare functionaliteit. De accelerator() methode verhoogt de snelheid van de auto terwijl de rev_throttle() methode verhoogt de snelheid van de motorfiets. Insgelijks, apply_brakes() en pull_brake_lever() remt in de respectievelijke voertuigen. eindelijk, de assign_driver() en assign_rider() methoden wijzen de voertuigoperator toe.

Laten we vervolgens een klasse maken om toegang te krijgen tot deze verschillende methoden. Voeg eerst een . toe __init.py__ in dezelfde map die u hebt gemaakt car.py en motorcycle.py:

touch __init__.py

Voeg nu de volgende code toe aan een nieuw bestand: 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()

Dit script dat onze client- en serviceobjecten maakt. We importeren eerst de Car en Motorcycle klassen en maak er objecten mee. Dan roepen we de methodes op uit de bike voorwerp (Motorcycle klas). Daarna roepen we de methoden van de car voorwerp (Car klas). Wanneer uitgevoerd, zal alle tot nu toe genoemde code werken.

Er wordt echter een uitzondering gemaakt wanneer we proberen de methoden van de aan te roepen Car klas met de bike object. Wanneer we dit script uitvoeren:

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 dit geval kunnen we de Motorcycle klas of de drive.py script om de juiste methoden te gebruiken. In veel gevallen hebben we echter mogelijk geen toegang tot de broncode van de client of serviceklasse. Dit is ook een simpel voorbeeld. Bij grotere clients en services is het misschien niet haalbaar om een โ€‹โ€‹van beide te refactoren voor het geval we de compatibiliteit met andere systemen verbreken.

In plaats daarvan kunnen we een adapter gebruiken om de compatibiliteitskloof tussen onze klantcode en ons serviceobject te overbruggen.

Adapters gebruiken om het incompatibiliteitsprobleem op te lossen

In een nieuw bestand, motorcycle_adapter.py, voeg de volgende klasse toe:

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)

We hebben een MotorcycleAdapter klasse, die zichzelf instantieert met een serviceobject (motorcycle). De adapter implementeert de clientmethoden die zijn: accelerate(), apply_brakes() en assign_driver(). In het lichaam van de accelerate() methode hebben we de motorcycle instantie van het serviceobject om de . aan te roepen rev_throttle() service methode. Evenzo gebruiken de andere methoden de overeenkomstige methoden van de Motorcycle klasse.

Laten we nu updaten drive.py zodat we de adapter in de . kunnen gebruiken try/except blok:

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 is een object van de MotorcycleAdapter klas. Wij leverden de bike bezwaar maken tegen de MotorcycleAdapter klasse' constructeur. Het uitvoeren van dit script geeft ons de volgende uitvoer:

Bekijk onze praktische, praktische gids voor het leren van Git, met best-practices, door de industrie geaccepteerde normen en bijgevoegd spiekbriefje. Stop met Googlen op Git-commando's en eigenlijk leren het!

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

Zonder de onderliggende aan te passen Motorcycle klasse, we kunnen het laten werken als een Car met behulp van een adapter!

Voors en tegens van het ontwerppatroon van de adapter

De voordelen van adapterpatronen zijn:

  • We kunnen een lage koppeling tussen de adapterklasse en de klantklasse bereiken.
  • We kunnen de adapterklasse hergebruiken om tal van serviceklassen in de toepassing op te nemen.
  • We kunnen de flexibiliteit van het programma vergroten door meerdere adapters te introduceren zonder de klantcode te verstoren

De nadelen van Adapter Pattern zijn:

  • De complexiteit van het programma neemt toe met de toevoeging van adapterklasse en serviceklasse.
  • Er is een toename van de overhead in het programma omdat de verzoeken van de ene klasse naar de andere worden doorgestuurd.
  • Adapterpatroon (klasse-adapter) gebruikt meerdere overervingen, die mogelijk niet door alle programmeertalen worden ondersteund.

Conclusie

In dit artikel hebben we geleerd over het ontwerppatroon van de adapter, de typen en de problemen die ze oplossen. We hebben het adapterpatroon in Python geรฏmplementeerd, zodat we kunnen communiceren met a Motorcycle object, zoals een Car object door een adapter te gebruiken, zodat de interface van elke klasse niet verandert.

Tijdstempel:

Meer van Stapelmisbruik