Introduzione
I Modello di progettazione dell'adattatore è un popolare Modello di progettazione strutturale utilizzato nell'ingegneria del software. Questa guida esamina come possiamo implementare l'Adapter Design Pattern in Python.
Modelli di progettazione sono soluzioni simili a modelli, praticamente ricette per risolvere problemi ricorrenti e comuni nello sviluppo del software. L'Adattatore Pattern si basa sul concetto di adattatore del mondo reale! Ad esempio, il caricabatterie di un laptop potrebbe avere una spina a 3 pin all'estremità, ma la presa a muro potrebbe essere solo una presa a 2 pin. Per collegare un caricabatterie a 3 pin a questa presa, avremmo bisogno di un adattatore che accetti una spina a 3 pin e adatta l'interfaccia nel 2-pin zoccolo.
Hanno un caricabatterie a 2 pin e un caricabatterie a 3 pin la stessa funzione di base (condurre l'elettricità dalla presa al laptop), ma hanno una forma diversa, e si può facilmente adattare nell'altro. Ogni volta che si hanno componenti software con la stessa funzione di base ma forme diverse, è possibile applicare l'Adapter Design Pattern.
Il modello adattatore segue questo esatto principio. Consente a due interfacce incompatibili di lavorare insieme senza modificare gli interni di ciascun componente. Ciò si ottiene adattando un'interfaccia, a un'altra, esternamente.
Diamo un'occhiata alla terminologia di base prima di immergerci più a fondo nel mondo degli Adapter Patterns:
- Interfaccia client: un'interfaccia che specifica le funzioni che il client deve implementare.
- .: una classe che implementa l'interfaccia client.
- Adattato/servizio: la classe incompatibile che deve collaborare con l'interfaccia client.
- adattatore: La classe che rende possibile la collaborazione tra il servizio e il cliente.
Diversi tipi di modelli di adattatori
Il modello di progettazione dell'adattatore può essere implementato in due modi diversi:
Adattatore oggetto
Con questo metodo, la classe adapter implementa i metodi dall'interfaccia client. Pertanto, l'oggetto client e l'oggetto adattatore sono compatibili tra loro. L'oggetto del servizio forma a has-a
relazione con l'oggetto adattatore, ovvero l'oggetto servizio appartiene all'oggetto adattatore.
Sappiamo che la classe di servizio non è compatibile con il client. La classe dell'adattatore avvolge l'oggetto di servizio creando un'istanza con quell'oggetto. Ora è possibile accedere all'oggetto servizio tramite l'oggetto adattatore, consentendo al client di interagire con esso.
Possiamo implementare l'adattatore oggetti in tutti i moderni linguaggi di programmazione.
Adattatore di classe
Con questo metodo, l'adattatore ha un is-a
rapporto con la classe di servizio. In questo scenario, l'adattatore implementa i metodi richiesti dal client, ma eredita da più adattatori, dandogli la possibilità di chiamare direttamente le loro funzioni incompatibili. Il più grande svantaggio di questa variazione è che possiamo usarla solo nei linguaggi di programmazione che supportano l'ereditarietà multipla delle classi.
Implementazione dell'Adapter Design Pattern in Python
Nella sezione seguente, implementeremo il modello di progettazione dell'adattatore in Python, in particolare utilizzando il variazione dell'adattatore oggetto. La sezione è divisa in due parti. In primo luogo, creeremo l'ambiente in cui dovrebbe essere utilizzato il modello adattatore. È importante vedere chiaramente come questo modello può risolvere alcuni problemi software. La seconda sezione utilizzerà un adattatore per risolvere il problema.
Problema di incompatibilità tra classi
Esaminiamo il problema di compatibilità quando il client e la classe di servizio implementano funzionalità diverse. Crea una classe client con i seguenti metodi e salvala in una cartella come 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")
Qui abbiamo creato un Car
classe con tre metodi accelerate()
, apply_brakes()
ed assign_driver()
. Abbiamo importato il random
modulo e lo ha utilizzato per generare numeri che impostano la velocità dell'auto dopo l'accelerazione e l'applicazione dei freni. Il assign_driver()
il metodo visualizza il nome del conducente dell'auto.
Successivamente, dobbiamo creare una classe di servizio o adattatore che desideri collaborare con la classe client Car
. Crea una classe di moto come questa e salvala nella tua cartella come 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 classe di servizio, Motorcycle
viene creato sopra con tre metodi rev_throttle()
, pull_brake_lever()
e assign_rider()
. Notare la differenza tra i metodi del servizio e della classe client nonostante la loro funzionalità simile. Il accelerator()
il metodo aumenta la velocità dell'auto mentre il rev_throttle()
il metodo aumenta la velocità della moto. Allo stesso modo, apply_brakes()
ed pull_brake_lever()
aziona i freni nei rispettivi veicoli. Infine, il assign_driver()
ed assign_rider()
metodi assegnano l'operatore del veicolo.
Quindi, creiamo una classe per accedere a questi diversi metodi. Innanzitutto, aggiungi un __init.py__
nella stessa cartella che hai creato car.py
ed motorcycle.py
:
touch __init__.py
Ora aggiungi il seguente codice in un nuovo file 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()
Questo script che crea i nostri oggetti client e servizi. Per prima cosa importiamo il Car
ed Motorcycle
classi e creare oggetti con esse. Quindi invochiamo i metodi da bike
oggetto (Motorcycle
classe). Dopo, invochiamo i metodi di car
oggetto (Car
classe). Una volta eseguito, tutto il codice menzionato finora funzionerà.
Tuttavia, viene sollevata un'eccezione quando si tenta di invocare i metodi di Car
classe con il bike
oggetto. Quando eseguiamo questo 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'
In questo caso, possiamo modificare il Motorcycle
classe o il drive.py
script per utilizzare i metodi corretti. Tuttavia, in molti casi, potremmo non avere accesso al codice sorgente del client o della classe di servizio. Inoltre, questo è un semplice esempio. Con client e servizi più grandi, potrebbe non essere possibile eseguire il refactoring di nessuno dei due nel caso in cui si rompa la compatibilità con altri sistemi.
Invece, possiamo utilizzare un adattatore per colmare il divario di compatibilità tra il nostro codice client e il nostro oggetto di servizio.
Utilizzo di adattatori per risolvere il problema di incompatibilità
In un nuovo file, motorcycle_adapter.py
, aggiungi la seguente classe:
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)
Abbiamo creato una MotorcycleAdapter
class, che istanzia se stessa con un oggetto di servizio (motorcycle
). L'adattatore implementa i metodi client che sono accelerate()
, apply_brakes()
ed assign_driver()
. All'interno del corpo del accelerate()
metodo, abbiamo usato il motorcycle
istanza dell'oggetto servizio per chiamare il rev_throttle()
metodo di servizio. Allo stesso modo, gli altri metodi utilizzano i metodi corrispondenti di Motorcycle
classe.
Ora, aggiorniamo drive.py
quindi possiamo usare l'adattatore nel try/except
bloccare:
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()
Qui,bike_adapter
è un oggetto del MotorcycleAdapter
classe. Abbiamo fornito il bike
opporsi alla MotorcycleAdapter
costruttore di classe. L'esecuzione di questo script fornisce il seguente output:
Dai un'occhiata alla nostra guida pratica e pratica per l'apprendimento di Git, con le migliori pratiche, gli standard accettati dal settore e il cheat sheet incluso. Smetti di cercare su Google i comandi Git e in realtà imparare esso!
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
Senza dover aggiustare il sottostante Motorcycle
classe, possiamo farlo funzionare come a Car
usando un adattatore!
Pro e contro del modello di progettazione dell'adattatore
I vantaggi dei modelli di adattatore sono:
- Possiamo ottenere un basso accoppiamento tra la classe dell'adattatore e la classe del client.
- Possiamo riutilizzare la classe dell'adattatore per incorporare numerose classi di servizio nell'applicazione.
- Possiamo aumentare la flessibilità del programma introducendo più adattatori senza interferire con il codice client
Gli svantaggi di Adapter Pattern sono:
- La complessità del programma aumenta con l'aggiunta della classe dell'adattatore e della classe di servizio.
- Si verifica un aumento del sovraccarico nel programma poiché le richieste vengono inoltrate da una classe all'altra.
- Adapter Pattern (adattatore di classe) utilizza eredità multiple, che tutti i linguaggi di programmazione potrebbero non supportare.
Conclusione
In questo articolo, abbiamo appreso del modello di progettazione dell'adattatore, dei suoi tipi e dei problemi che risolvono. Abbiamo implementato l'Adapter Pattern in Python in modo da poter interagire con a Motorcycle
oggetto, come a Car
oggetto utilizzando un adattatore in modo che l'interfaccia di ogni classe non cambi.