Template Methodパターン

結城浩さんの「Java言語で学ぶデザインパターン入門」を題材にpythonデザインパターン
書いてみる。
今回はTemplate Methodパターン。
どんなものかというと、スーパークラスにテンプレートとなるメソッドが定義される。
ただその定義の中ではメソッドは実装されない。
メソッドを実装するのはサブクラスで、そこで具体的な処理が決まるというもの。
異なるサブクラスが異なる実装をすれば、異なる処理が行われけど、処理の大きな流れはスーパークラス
決めたとおりになる。
このように、スーパークラスで処理の大きな枠組みを決めておいて、サブクラスでその具体的処理を決める
デザインパターン
サンプルになっているのは、文字や文字列を5回繰り返して表示するもの(template.py)。
まずはAbstractDisplayクラス

class AbstractDisplay(object):
    def opening(self):
        pass

    def printing(self):
        pass

    def closing(self):
        pass

    def display(self):
        self.opening()     #openingメソッドの呼び出し
        for i in range(5):    #printingメソッドを5回呼び出し
            self.printing()
        self.closing()     #closingメソッドを呼び出し

ここでは「opening」、「printing」、「closing」の3つのメソッドを宣言して、
displayメソッドの中でその具体的な処理が決まっていない3つのメソッドを使っていて、
displayメソッドがテンプレートメソッドになる。
opening,printing,closingメソッドを実装するのはサブクラスが担う。
次はCharDisplayクラス

class CharDisplay(AbstractDisplay):
    def __init__(self, ch):
        self.ch = ch

    def opening(self):
        print "<<",

    def printing(self):
        print self.ch,

    def closing(self):
        print ">>"

ここではAbstractDisplayクラスを継承してopening,printing,closingメソッドを実装している。
つづいてStringDisplayクラス

class StringDisplay(AbstractDisplay):
    def __init__(self, st):
        self.st = st
        self.width = len(self.st)   #文字列の長さを出しておく

    def opening(self):       
        self.printLine()        #このクラスのメソッドprintLineで線を引く

    def printing(self):        
        print "|%s|" %(self.st)    #文字列の前後に"|"を付ける

    def closing(self):
        self.printLine()               #このクラスのメソッドprintLineで線を引く

    def printLine(self):               #printLineメソッドを実装
        print "+",           #枠の角の"+"を表示
        n = 0
        while n < self.width:     #文字列の長さの分の"-"を表示
            print "-",
            n += 1
        print  "+"           #枠の角の"+"を表示

ここでもAbstractDisplayクラスを継承してopening,printing,closingメソッドを実装している。
最後にCharDisplayクラスとStringDisplayクラスのインスタンスを作ってdisplayメソッドを呼ぶ。

d1 = CharDisplay("H")
d2 = StringDisplay("Hello, world.")
d1.display()
d2.display()

d1,d2はAbstractDisplayクラスのサブクラスのインスタンスだから継承したdisplayメソッドが使える。
さて、実行してみると、

C:\works\python\book1>python template.py
<< H H H H H >>
+ - - - - - - - - - - - - - +
|Hello, world.|
|Hello, world.|
|Hello, world.|
|Hello, world.|
|Hello, world.|
+ - - - - - - - - - - - - - +

あれ?文字の間にスペースができてしまってる。
どうやらprintを使うと出力直後に空白が一文字出てしまうためみたい。
そこで標準出力への書き出しに print ではなく sys.stdout.write() を使って書き直し。他にも短く書けるところを修正してみた。
修正CharDisplayクラス

import sys
class CharDisplay(AbstractDisplay):
    def __init__(self, ch):
        self.ch = ch

    def opening(self):
        print "<<",

    def printing(self):
        sys.stdout.write(self.ch)

    def closing(self):
        print ">>"

修正StringDisplayクラス

class StringDisplay(AbstractDisplay):
    def __init__(self, st):
        self.st = st
        self.width = len(self.st)

    def opening(self):
        self.printLine()

    def printing(self):
        print "|%s|" %(self.st)

    def closing(self):
        self.printLine()

    def printLine(self):
        sys.stdout.write("".join(("+","-"*self.width,"+\n")))

それでは実行してみると、

C:\works\python\book1>python template.py
<<HHHHH>>
+-------------+
|Hello, world.|
|Hello, world.|
|Hello, world.|
|Hello, world.|
|Hello, world.|
+-------------+

きれいに収まった。
同じメソッドを使っているけど実際の動作は個々のクラスのCharDisplayとStringDisplay
で決まっているわけだ。