Python의 어댑터 디자인 패턴

개요

XNUMXD덴탈의 어댑터 디자인 패턴 인기가있다. 구조 설계 패턴 소프트웨어 엔지니어링에 사용됩니다. 이 가이드에서는 Python에서 어댑터 디자인 패턴을 구현하는 방법을 살펴봅니다.

디자인 패턴 템플릿과 같은 솔루션입니다. 실제로 소프트웨어 개발에서 반복적으로 발생하는 일반적인 문제를 해결하기 위한 방법입니다. 어댑터 패턴은 실제 어댑터의 개념을 기반으로 합니다! 예를 들어, 노트북 충전기의 끝에는 3핀 플러그가 있지만 벽면 소켓은 2핀 소켓만 있을 수 있습니다. 3핀 충전기를 이 소켓에 꽂으려면 3핀 플러그를 수용하는 어댑터가 필요합니다. 적응하다 인터페이스 2 핀 소켓.

2핀 충전기와 3핀 충전기가 있습니다. 동일한 기본 기능 (소켓에서 노트북으로 전기를 전도) 다른 형태, 그리고 하나는 쉽게 각색하다 다른쪽으로. 기본 기능은 같지만 형태가 다른 소프트웨어 컴포넌트가 있을 때마다 어댑터 디자인 패턴을 적용할 수 있습니다.

어댑터 패턴은 이 정확한 원칙을 따릅니다. 이를 통해 두 개의 호환되지 않는 인터페이스가 각 구성 요소의 내부를 수정하지 않고도 함께 작동할 수 있습니다. 이것은 외부에서 하나의 인터페이스를 다른 인터페이스에 적용함으로써 달성됩니다.

어댑터 패턴의 세계로 더 깊이 들어가기 전에 몇 가지 기본 용어를 살펴보겠습니다.

  • 클라이언트 인터페이스: 클라이언트가 구현해야 하는 기능을 지정하는 인터페이스입니다.
  • Client: 클라이언트 인터페이스를 구현하는 클래스입니다.
  • 수혜자/서비스: 클라이언트 인터페이스와 협업해야 하는 호환되지 않는 클래스입니다.
  • 어댑터: 서비스와 클라이언트의 협업을 가능하게 하는 클래스.

다양한 유형의 어댑터 패턴

어댑터 디자인 패턴은 두 가지 방법으로 구현할 수 있습니다.

객체 어댑터

이 메서드를 사용하면 어댑터 클래스가 클라이언트 인터페이스의 메서드를 구현합니다. 따라서 클라이언트 개체와 어댑터 개체는 서로 호환됩니다. 서비스 객체는 has-a 어댑터 개체와의 관계, 즉 서비스 개체가 어댑터 개체에 속합니다.

서비스 클래스가 클라이언트와 호환되지 않는다는 것을 알고 있습니다. 어댑터 클래스는 해당 개체로 자신을 인스턴스화하여 서비스 개체를 래핑합니다. 이제 어댑터 개체를 통해 서비스 개체에 액세스할 수 있으므로 클라이언트가 이 개체와 상호 작용할 수 있습니다.

우리는 모든 최신 프로그래밍 언어에서 개체 어댑터를 구현할 수 있습니다.

클래스 어댑터

이 방법을 사용하면 어댑터가 is-a 서비스 클래스와의 관계. 이 시나리오에서 어댑터는 클라이언트에 필요한 메서드를 구현하지만 여러 어댑터에서 상속하므로 호환되지 않는 기능을 직접 호출할 수 있습니다. 이 변형의 가장 큰 단점은 클래스의 다중 상속을 지원하는 프로그래밍 언어에서만 사용할 수 있다는 것입니다.

Python에서 어댑터 디자인 패턴 구현

아래 섹션에서는 특히 다음을 사용하여 Python에서 어댑터 디자인 패턴을 구현합니다. 개체 어댑터 변형. 섹션은 두 부분으로 나뉩니다. 먼저 어댑터 패턴을 사용해야 하는 환경을 생성합니다. 이 패턴이 일부 소프트웨어 문제를 어떻게 해결할 수 있는지 명확하게 보는 것이 중요합니다. 두 번째 섹션에서는 어댑터를 사용하여 문제를 해결합니다.

클래스 간 비호환성 문제

클라이언트와 서비스 클래스가 서로 다른 기능을 구현할 때의 호환성 문제를 살펴보겠습니다. 다음 방법으로 클라이언트 클래스를 만들고 폴더에 다음과 같이 저장합니다. 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")

여기에서 우리는 Car XNUMX가지 메소드가 있는 클래스 accelerate(), apply_brakes()assign_driver(). 우리는 수입했다 random 모듈을 사용하여 가속 및 브레이크 적용 후 자동차의 속도를 설정하는 숫자를 생성하는 데 사용했습니다. 그만큼 assign_driver() 메소드는 자동차 운전자의 이름을 표시합니다.

다음으로 클라이언트 클래스와 협력하려는 서비스 또는 어댑터 클래스를 생성해야 합니다. Car. 이와 같은 오토바이 클래스를 만들고 폴더에 다음과 같이 저장합니다. 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")  

서비스 클래스, Motorcycle 위의 세 가지 방법으로 생성됩니다. rev_throttle(), pull_brake_lever()assign_rider(). 유사한 기능에도 불구하고 서비스와 클라이언트 클래스의 메소드의 차이점을 확인하십시오. 그만큼 accelerator() 방법은 자동차의 속도를 증가시키는 반면 rev_throttle() 방법은 오토바이의 속도를 증가시킵니다. 비슷하게, apply_brakes()pull_brake_lever() 각 차량에 브레이크를 적용합니다. 마지막으로, assign_driver()assign_rider() 방법은 차량 운영자를 할당합니다.

다음으로 이러한 다양한 메서드에 액세스할 수 있는 클래스를 만들어 보겠습니다. 먼저, 추가 __init.py__ 당신이 만든 같은 폴더에 car.pymotorcycle.py:

touch __init__.py

이제 새 파일에 다음 코드를 추가하십시오. 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()

클라이언트 및 서비스 개체를 생성하는 이 스크립트. 우리는 먼저 수입 CarMotorcycle 클래스와 함께 객체를 생성합니다. 그런 다음 다음에서 메서드를 호출합니다. bike 객체 (Motorcycle 수업). 그 후, 우리는 car 객체 (Car 수업). 실행되면 지금까지 언급한 모든 코드가 작동합니다.

그러나 의 메소드를 호출하려고 하면 예외가 발생합니다. Car 수업과 함께 bike 물체. 이 스크립트를 실행할 때:

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'

이 경우 수정할 수 있습니다. Motorcycle 수업 또는 drive.py 올바른 방법을 사용하는 스크립트. 그러나 많은 경우 클라이언트 또는 서비스 클래스의 소스 코드에 액세스할 수 없습니다. 또한 이것은 간단한 예입니다. 더 큰 클라이언트 및 서비스의 경우 다른 시스템과의 호환성이 깨질 경우에 대비하여 둘 중 하나를 리팩토링하는 것이 불가능할 수 있습니다.

대신 어댑터를 사용하여 클라이언트 코드와 서비스 개체 간의 호환성 격차를 해소할 수 있습니다.

어댑터를 사용하여 비호환성 문제 해결

새 파일에서 motorcycle_adapter.py, 다음 클래스를 추가하십시오.

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)

우리가 만든 MotorcycleAdapter 서비스 객체로 자신을 인스턴스화하는 클래스(motorcycle). 어댑터는 클라이언트 메소드를 구현합니다. accelerate(), apply_brakes()assign_driver(). 본체 내부에는 accelerate() 방법, 우리는 motorcycle 호출할 서비스 개체의 인스턴스 rev_throttle() 서비스 방법. 마찬가지로 다른 방법은 해당 방법을 사용합니다. Motorcycle 클래스입니다.

이제 업데이트를 해보자 drive.py 그래서 우리는 어댑터를 사용할 수 있습니다 try/except 블록 :

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

여기bike_adapter 의 대상이다 MotorcycleAdapter 수업. 우리는 공급 bike ~에 반대하다 MotorcycleAdapter 클래스' 생성자. 이 스크립트를 실행하면 다음과 같은 결과가 나옵니다.

모범 사례, 업계에서 인정하는 표준 및 포함된 치트 시트가 포함된 Git 학습에 대한 실습 가이드를 확인하십시오. 인터넷 검색 Git 명령을 중지하고 실제로 배움 이것!

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

기초를 조정할 필요 없이 Motorcycle 클래스, 우리는 그것을 작동시킬 수 있습니다 Car 어댑터를 사용하여!

어댑터 디자인 패턴의 장단점

어댑터 패턴의 장점은 다음과 같습니다.

  • 어댑터 클래스와 클라이언트 클래스 간의 낮은 결합을 달성할 수 있습니다.
  • 어댑터 클래스를 재사용하여 애플리케이션에 수많은 서비스 클래스를 통합할 수 있습니다.
  • 클라이언트 코드를 방해하지 않고 여러 어댑터를 도입하여 프로그램의 유연성을 높일 수 있습니다.

어댑터 패턴의 단점은 다음과 같습니다.

  • 프로그램의 복잡성은 어댑터 클래스와 서비스 클래스가 추가됨에 따라 증가합니다.
  • 요청이 한 클래스에서 다른 클래스로 전달될 때 프로그램의 오버헤드가 증가합니다.
  • 어댑터 패턴(클래스 어댑터)은 모든 프로그래밍 언어가 지원하지 않을 수 있는 다중 상속을 사용합니다.

결론

이 기사에서는 어댑터 디자인 패턴, 유형 및 해결하는 문제에 대해 배웠습니다. 우리는 Python에서 어댑터 패턴을 구현하여 다음과 상호 작용할 수 있습니다. Motorcycle 물건, 같은 Car 각 클래스의 인터페이스가 변경되지 않도록 어댑터를 사용하여 개체를 만듭니다.

타임 스탬프 :

더보기 스택카부스