[プレーンなデザインパターン] 8. アダプターパターンとPythonの例

専門的なプレゼンテーション:

クラスのインターフェイスをクライアントが必要とする別のインターフェイスに変換し、互換性のないインターフェイスのために連携できなかったクラスが連携できるようにします。

人気の紹介: 

実際には、既存のコンポーネントに基づいて適応作業を行うことになります。ユーザーは、必要な機能のほとんどを実行するために、特定のクラス A を呼び出すことに慣れている /呼び出してきた / しかできません。クラス A にはなく、クラス B にはある新しい関数が突然存在します。ユーザーにはさまざまな理由があります (ユーザーの削減)複雑さ、またはコストの問題など)は、クラス B を直接呼び出したくありません。新しい機能がクラス A を通じて直接使用できるのが最善です。

: 一部のコンピュータでは、既存のインターフェイスに互換性がないため、SD カードを直接読み取ることができず、変換にはカード リーダーが必要です。このロジックを抽出すると、2 つのオブジェクトの既存のインターフェイスには互換性がないが、互いの機能を使用したいため、互換性を強制すると多大なコストが発生し、低コストの適応ソリューションがすでに存在するため、適切なものを選択する必要があります。 . マッチング方式が最も合理的です。

アダプター パターンには次の役割が含まれます。

  • ターゲット: ターゲットの抽象クラス、クライアントが呼び出すことを予期する抽象インターフェイス (クラス)
  • Adapte: アダプター クラス、クライアントが呼び出すことを予期する実際のインターフェイス (クラス)
  • アダプタ: アダプタ クラス。クライアントによって直接呼び出されるインターフェイス (クラス)。互換性作業を完了するために、アダプタ クラスを継承または参照するために使用されます。

次のコードは、Adapter パターンを使用して上記の例を解決します。

コード:

# 适配器模式

import abc, six


# step-1:定义一个target目标抽象接口
@six.add_metaclass(abc.ABCMeta)
class SDCardTarget:
    @abc.abstractmethod
    def read_bytes(self, size, from_head=False) -> bytes:
        pass

    @abc.abstractmethod
    def write_bytes(self, data) -> bool:
        pass


# step-2: 定义一个外来的Adaptee真实接口,实现Target
class KingtonSDCard(SDCardTarget):
    _capacity = 8 * 1024 * 1024  # MB
    _data = bytearray(_capacity * bytearray(' ', 'utf8'))  # 申请一个长度为8MB的字节数组
    _curr_read_seek = 0
    _curr_write_seek = 0

    def read_bytes(self, size, from_head=False):
        """简易的读取SD卡实现"""
        if from_head:
            self._curr_read_seek = size
            return bytes(self._data[:size].rstrip(b' '))  # 返回字节串 b''

        if self._capacity <= (size + self._curr_read_seek):
            self._curr_read_seek = self._capacity
            return bytes(self._data[self._curr_seek:].rstrip(b' '))

        self._curr_read_seek += size
        return bytes(self._data[self._curr_read_seek:self._curr_read_seek + size].rstrip(b' '))

    def write_bytes(self, data: bytes):
        """简易的写SD卡实现"""
        if self._curr_write_seek + len(data) >= self._capacity:
            raise Exception('Insufficient space!')
        add_len = len(bytearray(data))
        self._data[self._curr_write_seek:self._curr_write_seek + add_len] = bytearray(data)
        self._curr_write_seek += add_len
        return True


# step-3: 定义一个Adapter适配器类,继承Adaptee
# 这个类是客户端(使用者)一直在用的,比上面的类先存在
class UniformDeviceOperationAPI:
    class MP3:
        pass

    class MP4:
        pass

    interface_map = {
        'mp3': MP3,  # 现有类已经支持的接口
        'mp4': MP4,  # 现有类已经支持的接口
        'sd_card': KingtonSDCard  # 适配器要适配的接口
    }

    def __init__(self, device_flag):
        self._dev_flag = None
        self._api_instance = None
        api_cls = self.interface_map.get(device_flag)
        if api_cls is not None:
            self._api_instance = api_cls()
            self._dev_flag = device_flag
        else:
            raise Exception('Unsupport device!')
        self._data = ''

    def existed_method_read_all(self):
        """已经支持的接口"""
        return self._data

    def adapted_method_read_sd_bytes(self, size, from_head=False):
        """适配好的操作SD接口"""
        return self._api_instance.read_bytes(size, from_head)

    def adapted_method_write_sd_bytes(self, data):
        """适配好的操作SD接口"""
        return self._api_instance.write_bytes(data)


if __name__ == '__main__':
    # client
    device_api = UniformDeviceOperationAPI('sd_card')

    data = device_api.adapted_method_read_sd_bytes(size=15)
    print('read data for size=15:', data)
    result = device_api.adapted_method_write_sd_bytes(b"this is data!")
    print('write data into sd card SUCC:', result)
    data = device_api.adapted_method_read_sd_bytes(size=15, from_head=True)
    print('read again data for size=15:', data)

コードでは、アダプターのインターフェイスのみがアダプター クラスで参照され、アダプター クラスからは継承されません。既存のコンポーネントには、他のクラスの継承など、すでに多くの機能が含まれている可能性があるため、既存のコンポーネントを拡張する場合は、既存の部分を変更しないようにする必要があります。

: アダプタ モードはクラス構造モードとオブジェクト構造モードに分かれており、コードは前者を実装しています。明らかにこの方法の方が手間がかかります。ターゲット クラスのすべてのメソッドを再度実装し、既存のコンポーネントと結合する必要があります。後者の実装は簡単ですが、既存のコンポーネントで api_instance 属性を直接宣言し、それをクライアントに公開して使用すると、クライアントはこの属性を通じてターゲット クラスのメソッドを直接呼び出します。

注: abc および 6 つのライブラリの基本的な使用法については、「 シンプルなファクトリ パターンと Python の例」の記事の最後にある概要を参照してください 。:)

メッセージを残してください~

 

 

参考記事:

http://c.biancheng.net/view/1354.html

おすすめ

転載: blog.csdn.net/sc_lilei/article/details/103381711