さるへい備忘録

さるへいのやったことを綴っているブログです。基本的にテクノロジーの話題です。

Pythonで学ぶデザインパターン入門 Template Methodパターン

結城先生の Java言語で学ぶデザインパターン入門 のコードを Python に書き直していくシリーズ第3段です。

Template Method パターンとは

文字通り、処理の大枠のテンプレートを作るパターンです。
具体的には、抽象クラスなどで処理のテンプレートを作成し、その詳細な実装は継承したクラスに任せるといったものになります。

ソースコード

コードの概要

「文字や文字列を5回繰り返して表示する」プログラムです。
抽象クラスである AbstractDisplaydisplay メソッドが定義されており、それを叶えるために3つのメソッドが定義されています。
そのメソッドの中身は何も定義されておらず、その処理をサブクラスに任せるといったものになります。

つまり、その処理次第で多種多様な display メソッドが構築できるといったことになります。

ソースコード

AbstractDisplay(処理のテンプレートが記載してある抽象クラス)

# coding: utf-8
from abc import ABCMeta, abstractmethod


class AbstractDisplay(metaclass=ABCMeta):

    @abstractmethod
    def open(self):
        pass

    @abstractmethod
    def print(self):
        pass

    @abstractmethod
    def close(self):
        pass

    def display(self):
        self.open()

        for i in range(5):
            self.print()

        self.close()

概要通りのコードですね。
display メソッドだけが具体的に記載されています。
それ以外のメソッドをサブクラスで実装しましょう。

CharDisplay(文字単体をdisplayする)

# coding: utf-8
from template_method.abstract_display import AbstractDisplay


class CharDisplay(AbstractDisplay):

    def __init__(self, ch: str):
        if len(ch) > 1:
            raise TypeError('文字は一文字')
        self.__ch = ch

    def open(self):
        print('<<', end='')

    def print(self):
        print(self.__ch, end='')

    def close(self):
        print('>>')

H を入力としたら <<HHHHH>> といった出力があることを期待するものとなります。
抽象クラスのものを素朴に継承してるだけですね。
Pythonのprintは勝手に改行されてしまうので、改行しないようには一工夫必要です。

StringDisplay(文字列をdisplayする)

# coding: utf-8
from template_method.abstract_display import AbstractDisplay


class StringDisplay(AbstractDisplay):

    def __init__(self, string: str):
        self.__string = string
        self.__width = len(string.encode('utf-8'))

    def open(self):
        self.print_line()

    def print(self):
        print('|%s|' % self.__string)

    def close(self):
        self.print_line()

    def print_line(self):
        print('+', end='')
        for i in range(self.__width):
            print('-', end='')
        print('+')

Hello World. を入力とすると

+------------+
|Hello World.|
|Hello World.|
|Hello World.|
|Hello World.|
|Hello World.|
+------------+

を期待するものです。
ここまでいじくってても、 open close print の3メソッドがあれば成り立ちます。

実行ファイル

# coding: utf-8
from template_method.char_display import CharDisplay
from template_method.string_display import StringDisplay

if __name__ == '__main__':
    d1 = CharDisplay('H')
    d2 = StringDisplay('Hello World.')
    d3 = StringDisplay('こんにちわ。')

    d1.display()
    d2.display()
    d3.display()

実際どう便利か

このパターンは簡単ですが、便利な図を想像しづらいかもしれません。
例えば、このソースコードのパターンで画像で同じ出力が欲しくなった時に上記の通りに実装するといった方針が立てられます。
その上で同じスーパークラスを継承してるので、型の制限もしやすいといったメリットもあります。

紹介したソースコードは簡単なロジックでしたが、もっと複雑になってくるとロジックがテンプレートとして用意されていることは可読性に非常に効いてくるので、とても便利ですよ。