LoginSignup
10
9

More than 5 years have passed since last update.

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

Posted at

設計を意識したコードが書けるようになる為に、デザインパターン修行しました。
他の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役のクラスを重点的に調べればよいことになり、プログラムのチェックがとても楽になるのと、既存のクラスの仕様だけがわかれば、新しいクラスを作ることができる。

参考

10
9
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
10
9