Model de proiectare a adaptorului în Python

Introducere

Model de design adaptor este un popular Model de proiectare structurală utilizat în inginerie software. Acest ghid analizează modul în care putem implementa modelul de design al adaptorului în Python.

Modele de design sunt soluții asemănătoare șablonului – practic rețete pentru rezolvarea problemelor recurente, comune în dezvoltarea de software. Modelul adaptorului se bazează pe conceptul unui adaptor din lumea reală! De exemplu, încărcătorul unui laptop poate avea o mufă cu 3 pini la capăt, dar priza de perete poate fi doar o priză cu 2 pini. Pentru a conecta un încărcător cu 3 pini la această priză, am avea nevoie de un adaptor, care acceptă o mufă cu 3 pini și se adaptează interfața în 2-pin priză.

Un încărcător cu 2 pini și un încărcător cu 3 pini au aceeași funcție de bază (conduce electricitatea de la priza la laptop), dar au o formă diferită, și se poate cu ușurință adapta în celălalt. Ori de câte ori aveți componente software cu aceeași funcție de bază, dar forme diferite, puteți aplica modelul de design al adaptorului.

Modelul adaptorului urmează exact acest principiu. Permite două interfețe incompatibile să lucreze împreună fără a modifica elementele interne ale fiecărei componente. Acest lucru se realizează prin adaptarea unei interfețe, la alta, extern.

Să ne uităm la terminologia de bază înainte de a ne scufunda mai adânc în lumea modelelor de adaptoare:

  • Interfață client: O interfață care specifică funcțiile pe care clientul ar trebui să le implementeze.
  • Client: O clasă care implementează interfața client.
  • Adaptat/Serviciu: Clasa incompatibilă care trebuie să colaboreze cu interfața client.
  • Adaptor: Clasa care face posibilă colaborarea între serviciu și client.

Diferite tipuri de modele de adaptoare

Modelul de proiectare al adaptorului poate fi implementat în două moduri diferite:

Adaptor de obiecte

Cu această metodă, clasa adaptorului implementează metodele din interfața client. Astfel, obiectul client și obiectul adaptor sunt compatibile între ele. Obiectul serviciu formează a has-a relația cu obiectul adaptor, adică obiectul de serviciu aparține obiectului adaptor.

Știm că clasa de servicii nu este compatibilă cu clientul. Clasa adaptorului se înfășoară în jurul obiectului de serviciu prin instanțierea cu acel obiect. Acum, obiectul de serviciu poate fi accesat prin obiectul adaptor, permițând clientului să interacționeze cu acesta.

Putem implementa adaptorul de obiecte în toate limbajele de programare moderne.

Adaptor de clasă

Cu această metodă, adaptorul are un is-a relația cu clasa de serviciu. În acest scenariu, adaptorul implementează metodele cerute de client, dar moștenește de la mai mulți adaptați, oferindu-i posibilitatea de a apela direct funcțiile lor incompatibile. Cel mai mare dezavantaj al acestei variante este că o putem folosi doar în limbajele de programare care acceptă moștenirea multiplă a claselor.

Implementarea modelului de proiectare a adaptorului în Python

În secțiunea de mai jos, vom implementa modelul de proiectare al adaptorului în Python, folosind în mod specific variație adaptor obiect. Secțiunea este împărțită în două părți. În primul rând, vom crea mediul în care ar trebui să fie utilizat modelul adaptorului. Este important să vedem clar cum acest model poate rezolva unele probleme de software. A doua secțiune va folosi un adaptor pentru a rezolva problema.

Problemă de incompatibilitate între clase

Să ne uităm la problema de compatibilitate atunci când clasa client și serviciu implementează funcționalități diferite. Creați o clasă de client cu următoarele metode și salvați-o într-un folder ca 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")

Aici, am creat un Car clasa cu trei metode accelerate(), apply_brakes() și assign_driver(). Am importat random modul și l-a folosit pentru a genera numere care stabilesc viteza mașinii după accelerarea și aplicarea frânelor. The assign_driver() metoda afișează numele șoferului mașinii.

În continuare, trebuie să creăm o clasă de serviciu sau adaptat care dorește să colaboreze cu clasa de client Car. Creați o clasă de motociclete ca aceasta și salvați-o în folderul dvs. ca 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")  

O clasă de servicii, Motorcycle este creat mai sus cu trei metode rev_throttle(), pull_brake_lever(), și assign_rider(). Observați diferența dintre metodele clasei de serviciu și client, în ciuda funcționalității lor similare. The accelerator() metoda crește viteza mașinii în timp ce rev_throttle() metoda crește viteza motocicletei. De asemenea, apply_brakes() și pull_brake_lever() acționează frânele în vehiculele respective. În cele din urmă, cel assign_driver() și assign_rider() metodele de atribuire a operatorului vehiculului.

În continuare, să creăm o clasă pentru a accesa aceste metode diferite. Mai întâi, adăugați un __init.py__ în același folder pe care l-ați creat car.py și motorcycle.py:

touch __init__.py

Acum adăugați următorul cod într-un fișier nou 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()

Acest script care creează obiectele noastre client și serviciu. Mai întâi importăm Car și Motorcycle clase și creați obiecte cu ele. Apoi invocăm metodele din bike obiect (Motorcycle clasă). După aceea, invocăm metodele car obiect (Car clasă). Când este executat, tot codul menționat până acum va funcționa.

Cu toate acestea, se ridică o excepție atunci când încercăm să invocăm metodele Car clasa cu bike obiect. Când rulăm acest 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'

În acest caz, putem modifica Motorcycle clasa sau cel drive.py script pentru a utiliza metodele potrivite. Cu toate acestea, în multe cazuri, este posibil să nu avem acces la codul sursă al clasei de client sau de servicii. De asemenea, acesta este un exemplu simplu. Cu clienți și servicii mai mari, s-ar putea să nu fie fezabil să refactorăm niciunul dintre aceștia în cazul în care întrerupem compatibilitatea cu alte sisteme.

În schimb, putem folosi un adaptor pentru a reduce decalajul de compatibilitate dintre codul nostru client și obiectul nostru de serviciu.

Utilizarea adaptoarelor pentru a rezolva problema de incompatibilitate

Într-un fișier nou, motorcycle_adapter.py, adăugați următoarea clasă:

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)

Am creat a MotorcycleAdapter clasa, care se instanțează cu un obiect de serviciu (motorcycle). Adaptorul implementează metodele client care sunt accelerate(), apply_brakes() și assign_driver(). În interiorul corpului accelerate() metoda, am folosit motorcycle instanță a obiectului de serviciu pentru a apela rev_throttle() metoda de serviciu. La fel, celelalte metode folosesc metodele corespunzătoare ale Motorcycle clasă.

Acum, să actualizăm drive.py astfel încât să putem folosi adaptorul în try/except bloc:

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

Aici,bike_adapter este un obiect al MotorcycleAdapter clasă. Noi am furnizat bike obiect la MotorcycleAdapter constructorul clasei. Executarea acestui script ne oferă următoarele rezultate:

Consultați ghidul nostru practic și practic pentru a învăța Git, cu cele mai bune practici, standarde acceptate de industrie și fisa de cheat incluse. Opriți căutarea pe Google a comenzilor Git și de fapt învăţa aceasta!

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

Fără a fi nevoie să ajusteze subiacentul Motorcycle clasa, o putem face să funcționeze ca un Car folosind un adaptor!

Avantaje și dezavantaje ale modelului de design al adaptorului

Avantajele modelelor de adaptoare sunt:

  • Putem realiza o cuplare scăzută între clasa adaptorului și clasa client.
  • Putem reutiliza clasa adaptorului pentru a încorpora numeroase clase de servicii în aplicație.
  • Putem crește flexibilitatea programului prin introducerea mai multor adaptoare fără a interfera cu codul clientului

Dezavantajele Adapter Pattern sunt:

  • Complexitatea programului crește odată cu adăugarea clasei de adaptor și a clasei de serviciu.
  • Există o creștere a cheltuielilor generale în program pe măsură ce cererile sunt transmise de la o clasă la alta.
  • Adapter Pattern (adaptorul de clasă) utilizează mai multe moșteniri, pe care toate limbajele de programare ar putea să nu le suporte.

Concluzie

În acest articol, am aflat despre modelul de design al adaptorului, tipurile acestuia și problemele pe care le rezolvă. Am implementat Adapter Pattern în Python, astfel încât să putem interacționa cu a Motorcycle obiect, ca un Car obiect folosind un adaptor astfel încât interfața fiecărei clase să nu se schimbe.

Timestamp-ul:

Mai mult de la Stackabuse