記事ディレクトリ
序文
私たちは日常生活の中で、「解決すべき問題に遭遇した。既存のツールでは問題を直接解決できない。例えば水道管を延長しなければならない。」といった状況に遭遇することがよくあります。距離要件は満たしていますが、既存の水道管の直径が異なるため、現時点では、直径の異なる 2 本の水道管を接続するアダプターが必要です。このような問題はよくあります。たとえば、コンピュータ画面に接続されているデータ ケーブルには、DVI、VGA、HDMI などのさまざまな形式があります。また、一端を VGA インターフェイスに接続するためにアダプタを使用する必要があることもよくあります。もう一方の端は HDMI インターフェースに接続されます。もう 1 つの例は家庭用電力です。電圧は電化製品が必要とする電圧であり、一定ではないため、このときに変換する必要もあります。
そして、プログラミングの世界でも、当然同様の問題に遭遇することになります。既存のプログラムは需要の問題のほとんどを解決できますが、直接使用することはできません。したがって、既存のプログラムと必要なプログラムを接続するにはアダプターが必要です。このモードをアダプター モードと呼び、アダプター モードとも呼ばれます。
モード紹介
クラスデザイン
アナログ現実 | 親切 | |
---|---|---|
実際の状況 | 大口径水道管 | バナークラス |
アダプタ | アダプタ | PrintBannerクラス |
需要状況 | 小径水道管 | 印刷インターフェース |
UML 風の図
コード
この例では、文字列を出力するための単純なクラスが使用されており、現在既存のクラス Banner があり、その機能は指定された文字列に括弧または * 記号を追加してコンソールに出力することです。ここで、括弧の追加と * の追加にそれぞれ対応する、指定された文字列の弱い表示と強い表示を必要とする新しい要件があるとします。また、Banner クラスのメソッドを直接使用することはできません。これら 2 つのメソッドは保護レベルに属しており、外部に公開されていないためです。したがって、クラスを作成し、Banner から継承して、このメソッドを開く必要があります。
バナークラス
"""
@Time: 2023/3/18 22:54
@Auth: CivilDog
@File: Banner.py
@IDE: PyCharm
@Motto: Nothing is impossible
"""
class Banner:
"""
打印字符串的类,类比现有程序
"""
def __init__(self, str_to_print: str):
self.__str_to_print = str_to_print
def _show_with_paren(self):
print("("+self.__str_to_print+")")
def _show_with_aster(self):
print("*"+self.__str_to_print+"*")
PrintInterface クラス
"""
@Time: 2023/3/18 22:57
@Auth: CivilDog
@File: PrintInterface.py
@IDE: PyCharm
@Motto: Nothing is impossible
"""
import abc
from abc import ABCMeta
class PrintInterface(metaclass=ABCMeta):
"""
打印类的接口类,抽象出需求所需要的方法,并由实际使用类所继承和实现
"""
@abc.abstractmethod
def print_weak(self):
pass
@abc.abstractmethod
def print_strong(self):
pass
PrintBannerクラス
"""
@Time: 2023/3/18 22:58
@Auth: CivilDog
@File: PrintBanner.py
@IDE: PyCharm
@Motto: Nothing is impossible
"""
from DesignMode.AdapterMode.PrintInterface import PrintInterface
from DesignMode.AdapterMode.Banner import Banner
class PrintBanner(PrintInterface, Banner):
"""
多重继承PrintInterface和Banner,实现父类定义的抽象方法,和开放protected级别的方法
"""
def __init__(self, str_to_print: str):
super(PrintBanner, self).__init__(str_to_print)
def print_weak(self):
self._show_with_paren()
def print_strong(self):
self._show_with_aster()
アダプタクライアントクラス
"""
@Auth: CivilDog
@File: AdapterClient.py
@IDE: PyCharm
@Motto: Nothing is impossible
"""
from DesignMode.AdapterMode.PrintBanner import PrintBanner
class AdapterClient:
"""
Adapter模式的测试入口
"""
@staticmethod
def run(*args, **kwargs):
str_to_print = "Hello World!"
p_b = PrintBanner(str_to_print)
p_b.print_weak()
p_b.print_strong()
if __name__ == '__main__':
AdapterClient.run()
出力結果
(Hello World!)
*Hello World!*
Process finished with exit code 0
委任を使用して多重継承を回避する
多重継承によって新たな問題が発生する可能性があり、トラブルシューティングが困難な場合が多いためです。そのため、プロジェクトによっては、多重継承を可能な限り回避する必要がある場合があります。
この時点で、委任メソッドに切り替えることができます。つまり、PrintBanner クラスの作業を他のクラス、つまり Banner クラスに委任することができます。このメソッドは、Banner クラス自体を公開せずに、必要なインターフェイスを開くこともできます。
デリゲーション手法は、プログラム開発において一般的なプログラミング手法でもあります。
バナークラス
"""
@Time: 2023/3/18 22:54
@Auth: CivilDog
@File: Banner.py
@IDE: PyCharm
@Motto: Nothing is impossible
"""
class Banner:
"""
打印字符串的类,类比现有程序
"""
def __init__(self, str_to_print: str):
self.__str_to_print = str_to_print
def show_with_paren(self):
print("("+self.__str_to_print+")")
def show_with_aster(self):
print("*"+self.__str_to_print+"*")
PrintBannerクラス
class PrintBanner(PrintInterface):
def __init__(self, str_to_print: str):
super(PrintBanner, self).__init__()
self.__banner = Banner(str_to_print)
def print_weak(self):
self.__banner.show_with_paren()
def print_strong(self):
self.__banner.show_with_aster()
要約する
アダプターパターンを使用する理由
上記は単純な例ですが、実際には、この要件を実現したい場合は、Banner クラスを直接変更してメソッドを公開することができます。なぜわざわざ 2 つの追加クラスを作成するのでしょうか?
元のコードへの影響を回避し、コストを削減します
この例は、アダプター パターンの使用方法を誰もが理解できるようにするためのものです。実際の開発状況と開発要件はこれよりもはるかに複雑です。当社の既存のプログラムは通常、厳格なテストを経てオンラインに公開されます。新しい要件のためにソース コードに直接基づいて変更が加えられた場合、再テストが必要になりますが、これはよりコストがかかり、より大きな影響を与える問題が発生する可能性があります。
アダプター モードを使用すると、この状況を回避できます。元のコードはテスト済みで比較的安定しており、すべてオンラインで実行されます。アダプテーションにより、既存のコードが新しいプロジェクトや新しい要件に使用されるため、問題が発生した場合でも、アダプテーションしたコードの問題であると判断しやすくなります。
新しいクラスを実装するにはインターフェイスのみが必要です
Python 言語はオープンソースですが、多くのコンパイル言語はオープンソースではない場合が多く、外部呼び出し用の API がいくつか提供されています。このとき、プロジェクトではサードパーティのものの露出を避けるために、通常、クラスを自分でカプセル化します。アダプターモードです。
バージョンアップでよく出てくる
ソフトウェアのライフサイクルには常にアップグレードが伴います。まれに、古いバージョンを完全に放棄することもできるため、古いバージョンと新しいバージョンの間の互換性の問題が頭痛の種になることがよくあります。アダプター モードを使用すると、この状況に簡単に対処できます。
アダプターモードを使用できないのはどのような場合ですか?
既存のプログラムと要件が完全に一致しない場合、当然そのプログラムは使用されず、新しいクラスを自分で実装する必要があります。
「イラストデザインパターン」結城宏