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.