Python
デザインパターン
DesignPatterns

デザインパターン(Design Pattern)#Adapter

More than 3 years have passed since last update.

設計を意識したコードが書けるようになる為に、デザインパターン修行しました。

他のDesign Patternもちょくちょく出していきます。


前置き

デザインパターンをどういう時に、何を、どう使うのかを理解することが一先ずの目標。

(Javaというか静的型付言語は初めてで、且つpython歴もそんなに長くないので、Pythonistaぽっくないところがあると思います。ご指摘ございましたらご教授ください。)

今回は、構造に関するパターンAdapter。


Adapterとは

Adapterパターンには以下の2種がある。

1. クラスによるAdapterパターン(継承を使ったもの)

2. インスタンスによるAdapterパターン(委譲を使ったもの)

2つのクラス間に変換構造を作り、別の用途に使えるように変換する。Apdaterは中間層の役割で、使う方と、使われる方は中身を知らなくても良い。要はクラスで返すのか、インスタンスで返すのかの違いかな。


概要

クラスによる継承Apapterパターンを使ったサンプルと委譲によるサンプルプログラムがあります。両者とも与えられた文字列を(文字列)や*文字列*のように表示します。

Bannerクラスには、文字列をカッコでくくって表示するshow_with_parenというメソッドと、文字列の前後に*印を付けて表示するshow_with_parenというメソッドが用意されています。Bannerクラスが既存のものだと仮定します。

Printインタフェースでは、文字列を弱く(カッコ付きで)表示するためのメソッドprint_weakと、文字列を強く表示するためのメソッドprint_strongが宣言されています。

アダプターの役割を担うのがPrinterBannerクラスです。このクラスは、提供されているBannerクラスを継承し、必要とされているPrinterインタフェースを実装する。これでPrinterBannerクラスはアダプターとしての機能を果たすことになります。


継承パターンのクラス図

Adapter継承


Adapter 継承パターン


banner.py

class Banner():

def __init__(self, string):
self.__string = string

def show_with_paren(self):
print('({0})').format(self.__string))

def show_with_aster(self):
print('({0})').format(self.__string))



printer.py

from abc import ABCMeta, abstractmethod

class Printer(metaclass=ABCMeta):

@abstractmethod
def print_weak(self):
pass

@abstractmethod
def print_strong(self):
pass



printer_banner.py

from banner import Banner

class PrinterBanner(Banner)

def __init__(self, string):
super().__init__(string)

def print_weak(self):
self.show_with_paren()

def print_strong(self):
self.show_with_aster()


PrintBannerクラスがAdapterの役割をしています。

Bannerクラスを継承して、show_with_parenメソッドとshow_with_asterメソッドを継承します。

更に、要求されているPrinterインタフェースをしてprint_weakメソッドとprint_strongメソッドを実装してます。



main.py

from printer_banner import PrinterBanner

if __name__ == '__main__':
pb = PrinterBanner('Bye')
pb.print_weak()
pb.print_strong()


Printerインタフェースを使ってprint_weak()とprint_strong()で出力している。mainの方では、PrinterBannerクラスがどのような実装になってるか知らないので、mainクラスを変更せずともPrinterBannerクラスの実装を変えることができる。


Adapter 委譲パターン

委譲によるサンプルプログラムはMainとBannerは継承パターンと同じです。異なるのはPrintBannerクラスだけ。つまり、Bannerクラスを利用して、Printerクラスと同じメソッドを持つクラスを実現しようという。

PrinterBannerクラスは、bannerフィールドでBannerクラスのインスタンスを保持します。このインスタンスはPrinterBannerクラスのコンストラクタで生成します。そして、print_weak及び、print_strongメソッドでは、そのbannerフィールドを介してshow_with_paren, show_with_asterメソッドを呼び出します。

継承を使った方だと、自分のスーパークラスから継承したshow_with_paren, show_with_asterメソッドを呼び出しますが、委譲のパターンではフィールド経由で呼び出しています。

PrinterBannerクラスのprint_weakメソッドが呼ばれた時、自分で処理するのではなく、別のインスタンス(Bannerのインスタンス)のshow_with_parenメソッドにお任せしている。これが委譲になります。


委譲パターンのクラス図

Adapter 委譲


printer_banner.py

from printer import Printer

from banner import Banner

class PrinterBanner(Printer):

def __init__(self, string):
self.__banner = Banner(string)

def print_weak(self):
self.banner.show_with_paren()

def print_strong(self):
self.banner.show_with_aster()


今度はPrinterクラスを継承して、まずBannerクラスのインスタンスを呼んでいる。

それで呼んだインスタンスに対してshow_with_parenメソッドとshow_with_asterメソッドを使ってprint_weakメソッドとprint_strongメソッドを実装している。

文言だけ変えてるけど、出力結果は一緒。


inheritance/main.py

(Bye)

*Bye*


delegation/main.py

(Hello)

*Hello*


まとめ

Adapterパターンは、既存のクラスに一皮かぶせて必要とするクラスを作る。つまり、インタフェースとなる部分が間に入って、異なる二者間のズレを埋め合わせる。

バグが出たとしても、既存のクラスにはバグがないことがわかっているのでAdapter役のクラスを重点的に調べればよいことになり、プログラムのチェックがとても楽になるのと、既存のクラスの仕様だけがわかれば、新しいクラスを作ることができる。


参考