読者です 読者をやめる 読者になる 読者になる

Adapterパターン


Rubyによるデザインパターン』(ラス・オルセン著 ピアソン・エデュケーション刊)の例をPythonに変換して書いています。


目次


かなりいい加減です。



アダプターというのは、たとえばUSB-PS2変換器の様に、インターフェイスのギャップを埋めるためのものです。

同じように、必要なメソッドが無いなどの不整合があるとき、ギャップを埋めるのが、
Adapterパターンです。




ここではprint文を例にとります。

print文は,

f = open("log.txt", "w")
print>>f, "hello world!"

の様に、出力先を変更できますが、実は >>の右に来るものは、
ファイルでなくても構いません。writeメソッドさえあればよいのです。


となるとTkinter.Textに出力したくなるのは自然ですね?

でも、Tkinter.Textにはwriteメソッドがありません・・・。

#encoding:shift-jis
from Tkinter import *

class TextWithWrite(object):
    def __init__(self, *a, **kw):
        self.text = Text(*a, **kw)
    
    def write(self, ss):
        self.text.insert(END, ss)
    
##    def insert(self, *a, **kw):
##        return self.text.insert(*a, **kw)
##    
##    def delete(self, *a, **kw):
##        return self.text.delete(*a, **kw)
##
##              ・・・
##
##    あーー!面倒くさい!!
    
    def __getattr__(self, name):
        return getattr(self.text, name)
    
root = Tk()
text_ww = TextWithWrite(root)
text_ww.pack()

print >> text_ww, "fizzbuzz"
print >> text_ww, "buzz"
print >> text_ww, "fizz"
text_ww.see(END)
root.mainloop()


Textにwriteメソッドを追加し、晴れてprint出来るようになりました。
今回追加したのはwrite1つだけですが、2個以上メソッドを追加するのも同様です。



ところで、TextWithIOはwrite以外はTextと同じですので、
他のメソッドはそのままself.textに委譲しなければいけません。

compare delete get index insert ... 等、数十個を一々書くのは面倒ですね!

そこで、__getattr__を使います。

    def __getattr__(self, name):
        return getattr(self.text, name)


text_ww.see のように、オブジェクトの属性にアクセスしようとしたが、
text_wwオブジェクトがseeを持っていないとき、__getattr__メソッドが呼ばれます。

nameはアクセスしようとした属性の名前(この場合"see")です。



そして、getattr関数でself.textのseeメソッドを取ってきます。


text_wwはcompareもdeleteもgetも・・・も持っていませんので、
text_ww.compare text_ww.delete text_ww.get 等とされる度に、__getattr__が呼ばれるのです。



さて、この後『Rubyによるデザインパターン』では、RubyらしいAdapterの代替法として、


Pythonでも同じ事は、出来ることは出来ますが、それはそれぞれの項で