KnowHow

技術的なメモを中心にまとめます。
検索にて調べることができます。

Python基礎 オブジェクト指向3(Adapter) 構造に関するデザインパターン

登録日 :2025/09/23 08:40
カテゴリ :Python基礎

Adapterとは

あるクラスのインターフェースを、そのクラスを利用する側が求める他のインターフェースへ変換するパターン。(API)

インターフェースに互換性のないクラス同士を組み合わせることができる。

構造に関するデザインパターン

Adapterの構成要素

  • Client
    あるクラスの機能を「利用する」側のクラス

  • Target
    Clientが必要とする機能のAPIを定義するインターフェース

  • Adapteeクラス(継承される元)
    利用される側のクラス
    Clientクラスと互換性ない。

  • Adapter
    Targetを実装するクラス
    Adapteeを継承する子クラス
    TargetのAPIを実装して、Adapteeの機能をClientが利用できるようにする

  • 実装方法

  • 継承をする
  • 移譲する
    可能であれば、2.移譲する方法を推奨する(SOLIDの原則を満たしやすい)

Adapterのメリット・デメリット

メリット
- 既存のクラス(Adaptee)を修正しないので再テストが不要になる
- 変換のためのコードをプログラムのビジネスロジックと分離できるので単一責任の原則に違反しない
- インターフェースを介してアダプタと連携するのでオープンクローズドの原則に違反しない

デメリット
- インターフェースやクラスが増えるので、小さなシステムなどではAdapteeを直接修正した方が良い場合もある

Adapterの使い所

既存のクラスを利用したが、そのインターフェースが利用したい側のコードと互換性がない場合

過去に十分テストされて実績のあるクラスに手を加えずに再利用したい場合

Adapteeのソースコードが手に入らない場合

Adapterの適用例

JSONデータをCSVに変換する例(継承を利用した実装)

Adapterの実装方法1(継承する)

サンプルコード

from abc import ABCMeta, abstractmethod
from typing import List, Dict


class Target(metaclass=ABCMeta):
    @abstractmethod
    def get_csv_data(self) -> str:
        pass


class NewLibrary(object):
    @staticmethod
    def get_json_data() -> List[Dict[str, str]]:
        return [
            {
                "data1": "Json_data_A",
                "data2": "Json_data_B",
            },
            {
                "data1": "Json_data_C",
                "data2": "Json_data_D",
            },
        ]


class JsonToCsvAdapter(NewLibrary, Target):
    def get_csv_data(self) -> str:
        json_data = self.get_json_data()

        header = ",".join(list(json_data[0].keys())) + "\n"
        body = "\n".join([",".join(list(d.values()))for d in json_data])

        return header + body


if __name__ == '__main__':
    adaptee = NewLibrary()
    print("=== Adaptee data")
    print(adaptee.get_json_data())
    print("")

    adapter = JsonToCsvAdapter()
    print("=== Adapter converted data")
    print(adapter.get_csv_data())
Adapterの実装方法2(権限委譲する)

委譲を使うことを推奨
サンプルコード

from abc import ABCMeta, abstractmethod
from typing import List, Dict


class Target(metaclass=ABCMeta):
    @abstractmethod
    def get_csv_data(self) -> str:
        pass


class NewLibrary(object):
    @staticmethod
    def get_json_data() -> List[Dict[str, str]]:
        return [
            {
                "data1": "Json_data_A",
                "data2": "Json_data_B",
            },
            {
                "data1": "Json_data_C",
                "data2": "Json_data_D",
            },
        ]


class JsonToCsvAdapter(Target):
    def __init__(self, _adaptee: NewLibrary):
        self.__adaptee = _adaptee

    def get_csv_data(self) -> str:
        json_data = self.__adaptee.get_json_data()

        header = ",".join(list(json_data[0].keys())) + "\n"
        body = "\n".join([",".join(list(d.values()))for d in json_data])

        return header + body


if __name__ == '__main__':
    adaptee = NewLibrary()
    print("=== Adaptee data")
    print(adaptee.get_json_data())
    print("")

    adapter = JsonToCsvAdapter(adaptee)
    print("=== Adapter converted data")
    print(adapter.get_csv_data())