Python

Python デザインパターンを学ぶ Adapterパターン

概要

オブジェクトのインターフェイスが、クライアントコードが要求するインターフェイスと一致しない場合、このパターンでは、プロキシ・インターフェイスを作成できる「アダプター」の使用を推奨している。非均質なオブジェクトのインターフェイスを均質化する際に有効。

次の実装例で、様々な種類のリソースをテキストリソースとして読めるように適応させるユースケースを示す。ここでは、バイナリデータ、ウェブベースのデータ、またはテキストデータを含むリソースオブジェクトを扱うことを想定する。これらのリソースはそれぞれ独自のタイプとインターフェースを持っているが、それらをすべてテキストタイプのオブジェクトとして読み取る必要がある。すべてのリソースタイプはテキストとして表現できるため、TextResourceAdapterを使ってインターフェースを均質化し、共通のread()メソッド(TextResourceのread()メソッドのように動作するように設定)を使ってテキスト表現を出力する。実装例では、サーバはテキストしか読めないことを想定(TextResourceクラスのみ、そのまま解釈できる)する。

BinaryResourceクラスは、一般的なクラスのインスタンスが、バイナリデータをラップすることを想定しているため、それを表現する。WebResourceクラスは、jsonのようなWebのデータを読み込むことを想定する。

TextResourceAdapterクラスは、client_resourceのテキスト表現を返すためにread()メソッドを使用するアダプタとして機能する。

実装例

class TextResource:
    def read(self):
        return "sample text"


class BinaryResource:

    def read_plain_text(self):
        return "sample text from binary"

    def read_raw(self):
        pass

    def read_interactive(self):
        pass


class WebResource:

    def read_json(self):
        return "sample text as json"

    def read_html(self):
        pass


class IncompatibleResourceError(Exception):
    pass


class TextResourceAdapter:

    convertibles = ("TextResource", "BinaryResource", "WebResource")

    def __init__(self, client_resource):
        self._verify_compatibility(client_resource)
        self._client_resource = client_resource

    def read(self):

        if self._client_resource.__class__ is BinaryResource:
            return self._client_resource.read_plain_text()

        elif self._client_resource.__class__ is WebResource:
            return self._client_resource.read_json()

        return self._client_resource.read()

    def _verify_compatibility(self, resource):
        if resource.__class__.__name__ not in self.__class__.convertibles:
            raise IncompatibleResourceError("{} cannot be adapted.".format(
                resource.__class__.__name__))


if __name__ == "__main__":

    client_resources = [BinaryResource(), WebResource(), TextResource()]

    for resource in client_resources:

        print("Adapting {} as a text resource...".format(
            resource.__class__.__name__))

        adapted_resource = TextResourceAdapter(resource)

        # 読み取りが均一化されている
        print(adapted_resource.read() + "\n")