Python

Python デザインパターンを学ぶ Decoratorパターン

概要

注意:PythonのDecoratorとDecoratorパターンは別物。PythonのDecoratorは言語の機能であるのに対し、Decoratorパターンはどの言語でも実装可能である。Decoratorの機能の存在を考えると、PythonでDecoratorパターンを実装する理由が見つからない。

Decoratorパターンの主な目的は、オブジェクトに動作や属性を追加するために、継承に代わる、より柔軟な方法を可能にすること。従来の実装では、オブジェクトを再帰的に自身の型の子でラップする(これにより、メソッドコールをラップしたり、関数コールの前後に振る舞いを追加する)。

  • 継承を避けるのではなく、一貫したインターフェイスを維持するために継承を利用する。そのため、内部状態やアイデンティティの管理が非常に複雑になる。
  • ラップされたオブジェクトのメソッドを呼び出すために、Decoratorクラスは、ラップされたクラスのすべてのメソッドを実装する必要がある。

以下の実装例では、 Decorator機能を使ってこの欠点を克服しようとする。

validate_inputs デコレータはクラス全体をラップし、特定のメソッド(ここでは’input_’で始まるもの)のみをデコレーションする。このデコレータには、対象となるメソッドのデコレータとして設定された関数のリストが渡される。また、メソッドに設定されたデコレータの順序も維持される。この関数は、前述のような欠点がなく、多重継承に代わるより柔軟な手法として使用できる。

実装例

def validate_inputs(decorators):
    def decorate(cls):
        for method in dir(cls):
            if callable(getattr(cls, method)) and method.startswith("input_"):
                for decorator in reversed(decorators):
                    setattr(cls, method, decorator(getattr(cls, method)))
        return cls
    return decorate


def is_string(func):
    def wrapper(self, text):
        if type(text) is not str:
            return "'{}' is invalid. ".format(text)
        return func(self, text)
    return wrapper


def is_lowercase(func):
    def wrapper(self, text):
        if not text.islower():
            return "'{}' is invalid. ".format(text)
        return func(self, text)
    return wrapper


@validate_inputs([is_string, is_lowercase])
class Form():
    def input_name(self, name):
        return "'{}' is valid.".format(name)


if __name__ == "__main__":

    form = Form()
    print(form.input_name("TestName"))
    print(form.input_name("testname"))
    print(form.input_name(42))