Wzorzec projektowy adaptera w Pythonie

Wprowadzenie

Połączenia Wzór projektu adaptera Jest popularny Wzór projektu strukturalnego stosowane w inżynierii oprogramowania. W tym przewodniku omówiono, jak możemy zaimplementować wzorzec projektowy adaptera w języku Python.

Wzory projektowe są rozwiązaniami szablonowymi – praktycznie receptami na rozwiązywanie powtarzających się, powszechnych problemów w tworzeniu oprogramowania. Wzorzec adaptera opiera się na koncepcji adaptera w świecie rzeczywistym! Na przykład ładowarka laptopa może mieć na końcu 3-pinową wtyczkę, ale gniazdko ścienne może być tylko 2-pinowe. Aby podłączyć ładowarkę 3-pinową do tego gniazda, potrzebowalibyśmy adaptera, który akceptuje wtyczkę 3-pinową, i dostosowuje się interfejs do 2-pin gniazdo elektryczne.

Ładowarka 2-pinowa i ładowarka 3-pinowa mają ta sama podstawowa funkcja (przewodzi prąd z gniazdka do laptopa), ale ma inna forma, i łatwo można przystosować w drugą. Zawsze, gdy masz komponenty oprogramowania z tą samą podstawową funkcją, ale różnymi formami, możesz zastosować wzorzec projektowy adaptera.

Wzór adaptera jest zgodny z tą zasadą. Umożliwia współpracę dwóch niekompatybilnych interfejsów bez modyfikowania elementów wewnętrznych każdego komponentu. Osiąga się to poprzez zewnętrzne dostosowanie jednego interfejsu do drugiego.

Przyjrzyjmy się podstawowej terminologii, zanim zagłębimy się w świat wzorców adapterów:

  • Interfejs klienta: Interfejs określający funkcje, które klient powinien zaimplementować.
  • klientem: Klasa implementująca interfejs klienta.
  • Adaptator/Usługa: Niezgodna klasa, która musi współpracować z interfejsem klienta.
  • Zasilacz: Klasa, która umożliwia współpracę między usługą a klientem.

Różne typy wzorców adapterów

Wzorzec projektowy adaptera można zaimplementować na dwa różne sposoby:

Adapter obiektowy

Za pomocą tej metody klasa adaptera implementuje metody z interfejsu klienta. W ten sposób obiekt klienta i obiekt adaptera są ze sobą kompatybilne. Obiekt usługi tworzy a has-a związek z obiektem adaptera, tzn. obiekt usługi należy do obiektu adaptera.

Wiemy, że klasa usługi nie jest zgodna z klientem. Klasa adaptera otacza obiekt usługi, tworząc jej wystąpienie z tym obiektem. Teraz dostęp do obiektu usługi można uzyskać za pośrednictwem obiektu adaptera, umożliwiając klientowi interakcję z nim.

Adapter obiektowy możemy zaimplementować we wszystkich nowoczesnych językach programowania.

Adapter klasy

Dzięki tej metodzie adapter ma is-a związek z klasą usług. W tym scenariuszu adapter implementuje metody wymagane przez klienta, ale dziedziczy z wielu adapterów, co daje mu możliwość bezpośredniego wywoływania ich niezgodnych funkcji. Największą wadą tej odmiany jest to, że możemy jej używać tylko w językach programowania obsługujących wielokrotne dziedziczenie klas.

Implementacja wzorca projektowego adaptera w Pythonie

W poniższej sekcji zaimplementujemy wzorzec projektowy Adaptera w Pythonie, używając w szczególności odmiana adaptera obiektów. Sekcja podzielona jest na dwie części. Najpierw stworzymy środowisko, w którym należy użyć wzorca adaptera. Ważne jest, aby wyraźnie zobaczyć, w jaki sposób ten wzorzec może rozwiązać niektóre problemy z oprogramowaniem. Druga sekcja użyje adaptera do rozwiązania problemu.

Problem niezgodności między klasami

Przyjrzyjmy się problemowi kompatybilności, gdy klient i klasa usługi implementują różne funkcjonalności. Utwórz klasę klienta za pomocą następujących metod i zapisz ją w folderze jako 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")

Tutaj stworzyliśmy Car klasa z trzema metodami accelerate(), apply_brakes() i assign_driver(). Sprowadziliśmy random i użył go do wygenerowania liczb, które ustawiają prędkość samochodu po przyspieszeniu i naciśnięciu hamulców. The assign_driver() metoda wyświetla nazwisko kierowcy samochodu.

Następnie musimy stworzyć usługę lub klasę adaptacyjną, która chce współpracować z klasą klienta Car. Utwórz taką klasę motocykla i zapisz ją w swoim folderze jako 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")  

klasa usług, Motorcycle jest tworzony powyżej trzema metodami rev_throttle(), pull_brake_lever(), assign_rider(). Zwróć uwagę na różnicę między metodami usługi i klasy klienta, pomimo ich podobnej funkcjonalności. The accelerator() metoda zwiększa prędkość samochodu, podczas gdy rev_throttle() metoda zwiększa prędkość motocykla. Podobnie, apply_brakes() i pull_brake_lever() stosuje hamulce w odpowiednich pojazdach. Wreszcie assign_driver() i assign_rider() metody przypisują operatora pojazdu.

Następnie utwórzmy klasę, aby uzyskać dostęp do tych różnych metod. Najpierw dodaj __init.py__ w tym samym folderze, który utworzyłeś car.py i motorcycle.py:

touch __init__.py

Teraz dodaj następujący kod w nowym pliku 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()

Ten skrypt, który tworzy nasze obiekty klienta i usługi. Najpierw importujemy Car i Motorcycle klasy i tworzyć z nich obiekty. Następnie przywołujemy metody z bike obiekt (Motorcycle klasa). Następnie przywołujemy metody car obiekt (Car klasa). Po wykonaniu cały kod, o którym mowa do tej pory, będzie działał.

Jednak wyjątek powstaje, gdy próbujemy wywołać metody Car klasa z bike obiekt. Kiedy uruchamiamy ten skrypt:

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'

W takim przypadku możemy zmodyfikować Motorcycle klasa lub drive.py skrypt, aby użyć właściwych metod. Jednak w wielu przypadkach możemy nie mieć dostępu do kodu źródłowego klasy klienta lub usługi. To także prosty przykład. W przypadku większych klientów i usług refaktoryzacja któregokolwiek z nich może nie być możliwa na wypadek utraty kompatybilności z innymi systemami.

Zamiast tego możemy użyć adaptera, aby wypełnić lukę kompatybilności między naszym kodem klienta a naszym obiektem usługi.

Używanie adapterów do rozwiązania problemu z niezgodnością

W nowym pliku motorcycle_adapter.py, dodaj następującą klasę:

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)

Stworzyliśmy MotorcycleAdapter klasa, która inicjuje się z obiektem usługi (motorcycle). Adapter implementuje metody klienta, które są accelerate(), apply_brakes() i assign_driver(). Wewnątrz ciała accelerate() metody, użyliśmy motorcycle wystąpienie obiektu usługi do wywołania rev_throttle() sposób obsługi. Podobnie inne metody wykorzystują odpowiednie metody Motorcycle class.

Teraz zaktualizujmy drive.py dzięki czemu możemy użyć adaptera w 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()

Tutaj,bike_adapter jest przedmiotem MotorcycleAdapter klasa. Dostarczyliśmy bike sprzeciw wobec MotorcycleAdapter Konstruktor klasy. Wykonanie tego skryptu daje nam następujące dane wyjściowe:

Zapoznaj się z naszym praktycznym, praktycznym przewodnikiem dotyczącym nauki Git, zawierającym najlepsze praktyki, standardy przyjęte w branży i dołączoną ściągawkę. Zatrzymaj polecenia Google Git, a właściwie uczyć się to!

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

Bez konieczności dostosowywania instrumentu bazowego Motorcycle klasy, możemy sprawić, by działał jak Car za pomocą adaptera!

Plusy i minusy wzorca projektowego adaptera

Zaletami wzorców adapterów są:

  • Możemy osiągnąć niskie sprzężenie między klasą adaptera a klasą klienta.
  • Możemy ponownie użyć klasy adaptera, aby włączyć do aplikacji wiele klas usług.
  • Możemy zwiększyć elastyczność programu, wprowadzając wiele adapterów bez ingerencji w kod klienta

Wady wzorca adaptera to:

  • Złożoność programu wzrasta wraz z dodaniem klasy adaptera i klasy usługi.
  • W programie wzrasta obciążenie, ponieważ żądania są przekazywane z jednej klasy do drugiej.
  • Wzorzec adaptera (adapter klasy) używa wielu dziedziczeń, których wszystkie języki programowania mogą nie obsługiwać.

Wnioski

W tym artykule dowiedzieliśmy się o wzorcu projektowym adaptera, jego typach i rozwiązywanych przez niego problemach. Zaimplementowaliśmy wzorzec adaptera w Pythonie, abyśmy mogli wchodzić w interakcje z Motorcycle obiekt, jak a Car obiekt za pomocą adaptera, dzięki czemu interfejs każdej klasy nie ulega zmianie.

Znak czasu:

Więcej z Nadużycie stosu