Python のアダプター設計パターン

概要

  アダプターデザインパターン 人気があります 構造設計パターン ソフトウェア工学で使用されます。 このガイドでは、Python でアダプター デザイン パターンを実装する方法について説明します。

設計パターン テンプレートのようなソリューション - ソフトウェア開発で繰り返し発生する一般的な問題を解決するための実質的なレシピです。 アダプター パターンは、実際のアダプターの概念に基づいています。 たとえば、ラップトップの充電器の端に 3 ピン プラグが付いていても、壁のコンセントは 2 ピン ソケットしかない場合があります。 3 ピン充電器をこのソケットに差し込むには、3 ピン プラグを受け入れるアダプターが必要です。 適応する へのインターフェース 2ピン ソケット。

2 ピン充電器と 3 ピン充電器には 基本機能は同じ (ソケットからラップトップに電気を伝導します)が、持っています 別のフォーム、そして簡単に 適応する もう一方に。 基本機能は同じで形式が異なるソフトウェア コンポーネントがある場合は、いつでもアダプタ デザイン パターンを適用できます。

Adapter パターンは、まさにこの原則に従います。 これにより、各コンポーネントの内部を変更することなく、互換性のない XNUMX つのインターフェイスを連携させることができます。 これは、あるインターフェースを別のインターフェースに外部的に適合させることによって実現されます。

Adapter Patterns の世界に深く入り込む前に、いくつかの基本的な用語を見てみましょう。

  • クライアント インターフェイス: クライアントが実装する必要がある機能を指定するインターフェイス。
  • クライアント: クライアント インターフェイスを実装するクラス。
  • 適応者/サービス: クライアント インターフェイスと連携する必要がある非互換クラス。
  • アダプタ: サービスとクライアントの連携を可能にするクラス。

さまざまな種類のアダプター パターン

アダプターの設計パターンは、次の XNUMX つの方法で実装できます。

オブジェクトアダプタ

このメソッドを使用して、アダプタ クラスはクライアント インターフェイスからのメソッドを実装します。 したがって、クライアント オブジェクトとアダプタ オブジェクトは相互に互換性があります。 サービス オブジェクトは、 has-a アダプタ オブジェクトとの関係。つまり、サービス オブジェクトはアダプタ オブジェクトに属します。

サービス クラスがクライアントと互換性がないことがわかっています。 アダプター クラスは、サービス オブジェクトを使用して自身をインスタンス化することにより、サービス オブジェクトをラップします。 これで、アダプタ オブジェクトを介してサービス オブジェクトにアクセスできるようになり、クライアントがサービス オブジェクトと対話できるようになります。

オブジェクト アダプターは、最新のすべてのプログラミング言語で実装できます。

クラスアダプタ

この方法では、アダプターは is-a サービスクラスとの関係。 このシナリオでは、アダプタはクライアントが必要とするメソッドを実装しますが、複数のアダプティから継承するため、互換性のない関数を直接呼び出すことができます。 このバリエーションの最大の欠点は、クラスの多重継承をサポートするプログラミング言語でしか使用できないことです。

Python でのアダプター設計パターンの実装

以下のセクションでは、特に Python で Adapter デザイン パターンを実装します。 オブジェクト アダプタのバリエーション. このセクションは XNUMX つの部分に分かれています。 まず、Adapter Pattern を使用する環境を作成します。 このパターンがソフトウェアの問題をどのように解決できるかを明確に理解することが重要です。 XNUMX 番目のセクションでは、アダプターを使用して問題を解決します。

クラス間の非互換性の問題

クライアントとサービス クラスが異なる機能を実装する場合の互換性の問題を見てみましょう。 次のメソッドを使用してクライアント クラスを作成し、それをフォルダーに保存します。 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 上記の XNUMX つの方法で作成されます rev_throttle(), pull_brake_lever(), assign_rider(). 機能は似ていますが、サービス クラスとクライアント クラスのメソッドの違いに注意してください。 の accelerator() メソッドは車の速度を上げますが、 rev_throttle() メソッドはオートバイの速度を上げます。 同じく、 apply_brakes() & pull_brake_lever() それぞれの車両にブレーキをかけます。 最後に、 assign_driver() & assign_rider() メソッドは、車両のオペレーターを割り当てます。

次に、これらのさまざまなメソッドにアクセスするためのクラスを作成しましょう。 まず、 __init.py__ 作成した同じフォルダに car.py & motorcycle.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()

クライアントとサービス オブジェクトを作成するこのスクリプト。 最初にインポートします Car & Motorcycle クラスを作成し、それらを使用してオブジェクトを作成します。 次に、からメソッドを呼び出します 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 アダプターを使って!

アダプター設計パターンの長所と短所

アダプター パターンの利点は次のとおりです。

  • アダプタ クラスとクライアント クラスの間の低結合を実現できます。
  • アダプタ クラスを再利用して、多数のサービス クラスをアプリケーションに組み込むことができます。
  • クライアントコードに干渉することなく複数のアダプターを導入することで、プログラムの柔軟性を高めることができます

Adapter パターンの欠点は次のとおりです。

  • アダプタ クラスとサービス クラスが追加されると、プログラムの複雑さが増します。
  • 要求があるクラスから別のクラスに転送されるため、プログラムのオーバーヘッドが増加します。
  • アダプター パターン (クラス アダプター) は複数の継承を使用しますが、すべてのプログラミング言語がサポートしているとは限りません。

まとめ

この記事では、アダプターの設計パターン、そのタイプ、およびそれらが解決する問題について学びました。 と対話できるように、Python で Adapter パターンを実装しました。 Motorcycle オブジェクト、のような Car 各クラスのインターフェースが変わらないように、アダプタを使用してオブジェクトを作成します。

タイムスタンプ:

より多くの スタックアバス