Adapterdesignmønster i Python

Introduksjon

De Adapterdesignmønster er en populær Strukturelt designmønster brukes i programvareutvikling. Denne guiden ser på hvordan vi kan implementere Adapter Design Pattern i Python.

Design mønstre er mallignende løsninger – praktisk talt oppskrifter for å løse tilbakevendende, vanlige problemer innen programvareutvikling. Adaptermønsteret er basert på konseptet med en virkelig adapter! For eksempel kan laderen til en bærbar datamaskin ha en 3-pinners plugg i enden, men stikkontakten kan bare være en 2-pinners stikkontakt. For å koble en 3-pinners lader til denne kontakten, trenger vi en adapter som godtar en 3-pinners plugg, og tilpasser grensesnittet inn i 2-pin socket.

En 2-pinners lader og en 3-pinners lader har samme grunnleggende funksjon (leder strøm fra stikkontakten til den bærbare datamaskinen), men har en annen form, og man kan enkelt tilpasse inn i den andre. Når du har programvarekomponenter med samme grunnleggende funksjon, men forskjellige former, kan du bruke Adapter Design Pattern.

Adaptermønsteret følger nøyaktig dette prinsippet. Den lar to inkompatible grensesnitt fungere sammen uten å endre innsiden av hver komponent. Dette oppnås ved å tilpasse ett grensesnitt til et annet eksternt.

La oss se på noen grunnleggende terminologier før vi dykker dypere inn i verden av adaptermønstre:

  • Klientgrensesnitt: Et grensesnitt som spesifiserer funksjonene som klienten skal implementere.
  • kunde: En klasse som implementerer klientgrensesnittet.
  • Adaptee/Service: Den inkompatible klassen som må samarbeide med klientgrensesnittet.
  • adapter: Klassen som gjør samarbeidet mellom tjenesten og oppdragsgiver mulig.

Ulike typer adaptermønstre

Adapterdesignmønsteret kan implementeres på to forskjellige måter:

Objektadapter

Med denne metoden implementerer adapterklassen metodene fra klientgrensesnittet. Dermed er klientobjektet og adapterobjektet kompatible med hverandre. Tjenesteobjektet danner en has-a forhold til adapterobjektet dvs. tjenesteobjektet tilhører adapterobjektet.

Vi vet at serviceklassen ikke er kompatibel med klienten. Adapterklassen omslutter tjenesteobjektet ved å instansiere seg selv med det objektet. Nå kan tjenesteobjektet nås gjennom adapterobjektet, slik at klienten kan samhandle med det.

Vi kan implementere objektadapteren i alle moderne programmeringsspråk.

Klasseadapter

Med denne metoden har adapteren en is-a forhold til serviceklassen. I dette scenariet implementerer adapteren metodene som kreves av klienten, men den arver fra flere adaptere, noe som gir den muligheten til å kalle deres inkompatible funksjoner direkte. Den største ulempen med denne varianten er at vi bare kan bruke den i programmeringsspråkene som støtter flere klasser.

Implementering av adapterdesignmønsteret i Python

I avsnittet nedenfor vil vi implementere Adapter-designmønsteret i Python, spesielt ved å bruke objektadaptervariasjon. Seksjonen er delt i to deler. Først vil vi skape miljøet der adaptermønsteret skal brukes. Det er viktig å se tydelig hvordan dette mønsteret kan løse noen programvareproblemer. Den andre delen vil bruke en adapter for å løse problemet.

Inkompatibilitetsproblem mellom klasser

La oss se på kompatibilitetsproblemet når klienten og tjenesteklassen implementerer forskjellige funksjoner. Opprett en klientklasse med følgende metoder og lagre den i en mappe som 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")

Her har vi laget en Car klasse med tre metoder accelerate(), apply_brakes() og assign_driver(). Vi importerte random modul og brukte den til å generere tall som setter bilens hastighet etter å ha akselerert og satt på bremsene. De assign_driver() metoden viser bilførerens navn.

Deretter må vi opprette en tjeneste- eller tilpasset klasse som ønsker å samarbeide med klientklassen Car. Lag en motorsykkelklasse som denne og lagre den i mappen din som 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")  

En serviceklasse, Motorcycle er opprettet ovenfor med tre metoder rev_throttle(), pull_brake_lever()og assign_rider(). Legg merke til forskjellen mellom metodene for tjenesten og klientklassen til tross for deres lignende funksjonalitet. De accelerator() metode øker hastigheten på bilen mens rev_throttle() metode øker motorsykkelens hastighet. Like måte, apply_brakes() og pull_brake_lever() setter på bremser i de respektive kjøretøyene. Til slutt, den assign_driver() og assign_rider() metoder tildele kjøretøyoperatøren.

La oss deretter lage en klasse for å få tilgang til disse forskjellige metodene. Først legger du til en __init.py__ i samme mappe du opprettet car.py og motorcycle.py:

touch __init__.py

Legg nå til følgende kode i en ny fil 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()

Dette skriptet som lager våre klient- og tjenesteobjekter. Vi importerer først Car og Motorcycle klasser og lage objekter med dem. Deretter bruker vi metodene fra bike objekt (Motorcycle klasse). Etterpå bruker vi metodene til car objekt (Car klasse). Når den er utført, vil all koden som er nevnt så langt fungere.

Et unntak er imidlertid reist når vi prøver å påberope oss metodene til Car klasse med bike gjenstand. Når vi kjører dette skriptet:

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'

I dette tilfellet kan vi endre Motorcycle klassen eller drive.py skript for å bruke de riktige metodene. Men i mange tilfeller har vi kanskje ikke tilgang til kildekoden til klienten eller tjenesteklassen. Dessuten er dette et enkelt eksempel. Med større klienter og tjenester er det kanskje ikke mulig å refaktorere noen av dem i tilfelle vi bryter kompatibiliteten med andre systemer.

I stedet kan vi bruke en adapter for å bygge bro over kompatibilitetsgapet mellom klientkoden og tjenesteobjektet vårt.

Bruke adaptere for å løse inkompatibilitetsproblemet

I en ny fil, motorcycle_adapter.py, legg til følgende klasse:

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)

Vi har opprettet en MotorcycleAdapter klasse, som instansierer seg selv med et tjenesteobjekt (motorcycle). Adapteren implementerer klientmetodene som er accelerate(), apply_brakes() og assign_driver(). Inne i kroppen til accelerate() metoden har vi brukt motorcycle forekomst av tjenesteobjektet for å kalle rev_throttle() tjenestemetode. På samme måte bruker de andre metodene de tilsvarende metodene til Motorcycle klasse.

Nå, la oss oppdatere drive.py slik at vi kan bruke adapteren i try/except blokkere:

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

Herbike_adapter er et objekt av MotorcycleAdapter klasse. Vi leverte bike motsette seg MotorcycleAdapter klasses konstruktør. Å kjøre dette skriptet gir oss følgende utdata:

Sjekk ut vår praktiske, praktiske guide for å lære Git, med beste praksis, bransjeaksepterte standarder og inkludert jukseark. Slutt å google Git-kommandoer og faktisk lære den!

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

Uten å måtte justere det underliggende Motorcycle klasse, kan vi få det til å fungere som en Car ved hjelp av en adapter!

Fordeler og ulemper med adapterdesignmønster

Fordelene med adaptermønstre er:

  • Vi kan oppnå lav kobling mellom adapterklassen og klientklassen.
  • Vi kan gjenbruke adapterklassen for å inkludere en rekke serviceklasser i applikasjonen.
  • Vi kan øke fleksibiliteten til programmet ved å introdusere flere adaptere uten å forstyrre klientkoden

Ulempene med Adapter Pattern er:

  • Programmets kompleksitet øker med tillegg av adapterklasse og serviceklasse.
  • Det er en økning i overhead i programmet ettersom forespørslene videresendes fra en klasse til en annen.
  • Adapter Pattern (klasse adapter) bruker flere arv, som alle programmeringsspråk kanskje ikke støtter.

konklusjonen

I denne artikkelen lærte vi om adapterdesignmønsteret, dets typer og problemene de løser. Vi implementerte Adapter Pattern i Python slik at vi kan samhandle med en Motorcycle objekt, som en Car objekt ved å bruke en adapter slik at grensesnittet til hver klasse ikke endres.

Tidstempel:

Mer fra Stackabuse