LoginSignup
1
0

More than 1 year has passed since last update.

Pythonで振る舞いに関するデザインパターンを作成してみた

Posted at

目次

● Chain of Responsibility パターン
● Command パターン
● Interpreter パターン
● Iterator パターン
● Mediator パターン
● Memento パターン
● Observer パターン
● State パターン
● Strategy パターン
● Template Method パターン
● Visitor パターン
その他のデザインパターン

Chain of Responsibility パターン

ChainOfResponsibility.py
# ----------------------------------------------------------------------
#                 Chain of Responsibility パターン
# ----------------------------------------------------------------------
# 処理の責任をたらい回しにします。

from abc import ABCMeta, abstractmethod


class Handler(metaclass=ABCMeta):
    def __init__(self, next_handler=None):
        self.next_handler = next_handler

    @abstractmethod
    def handle(self, day):
        pass


class Handler01(Handler):
    def handle(self, day):
        if day <= 2:
            print('{}日の休暇を承認した。承認者:Handler01'.format(str(day)))
        else:
            self.next_handler.handle(day)


class Handler02(Handler):
    def handle(self, day):
        if 2 < day <= 5:
            print('{}日の休暇を承認した。承認者:Handler02'.format(str(day)))
        else:
            self.next_handler.handle(day)


class FinalHandler99(Handler):

    def handle(self, day):
        if 5 < day <= 10:
            print('{}日の休暇を承認した。承認者:FinalHandler99'.format(str(day)))
        else:
            print('11日以上の休暇を承認できない。承認者:FinalHandler99'.format(str(day)))


class Client:
    def __init__(self):
        h99 = FinalHandler99()
        h02 = Handler02(h99)
        self.handler = Handler01(h02)

    def get_handler(self):
        return self.handler


if __name__ == '__main__':
    h = Client().get_handler()
    for day in [2, 9, 3]:
        h.handle(day)

実行結果:

2日の休暇を承認した。承認者:Handler01
9日の休暇を承認した。承認者:FinalHandler99
3日の休暇を承認した。承認者:Handler02

Command パターン

Command.py
# ----------------------------------------------------------------------
#                 Command パターン
# ----------------------------------------------------------------------
# コマンドをクラスで表現します。

from abc import ABCMeta, abstractmethod


# 抽象コマンドクラス
class Command(metaclass=ABCMeta):
    def __init__(self, receiver):
        self.receiver = receiver

    @abstractmethod
    def execute(self):
        pass


# 具象コマンドクラス 
class StartCommand(Command):
    def execute(self):
        self.receiver.start()


# 具象コマンドクラス
class StopCommand(Command):
    def execute(self):
        self.receiver.stop()


# 具体的なアクションを定義するクラス
class Receiver:
    def start(self):
        print('処理内容: スタートアクション')

    def stop(self):
        print('処理内容: ストップアクション')


# コマンドを受け入れ、コマンドを実行するクラス
class Invoker:
    def __init__(self):
        self.commands = []

    def add_command(self, command):
        self.commands.append(command)

    def remove_command(self, command):
        self.commands.remove(command)

    def run_command(self):
        for cmd in self.commands:
            cmd.execute()


if __name__ == '__main__':
    receiver = Receiver()
    start_cmd = StartCommand(receiver)
    stop_cmd = StopCommand(receiver)
    inv = Invoker()
    inv.add_command(start_cmd)
    inv.add_command(stop_cmd)
    inv.run_command()

実行結果:

処理内容: スタートアクション
処理内容: ストップアクション

Interpreter パターン

Interpreter.py
# ----------------------------------------------------------------------
#                 Interpreter パターン
# ----------------------------------------------------------------------
# 構文を解析し、規則をクラスで表現します。

from abc import ABCMeta, abstractmethod


class Expression(metaclass=ABCMeta):
    @abstractmethod
    def interpret(self, context=None):  # contextは未使用
        pass


class ValueExpression(Expression):
    def __init__(self, value):
        self.value = value

    def interpret(self, context=None):
        return self.value


class OperatorExpression(Expression):
    def __init__(self, left, right):
        self.left = left
        self.right = right

    def interpret(self, context=None):
        pass


# 加算
class PlusExpression(OperatorExpression):
    def __init__(self, left, right):
        self.node = OperatorExpression(left, right)

    def interpret(self, context=None):
        return self.node.left.interpret() + self.node.right.interpret()


# 引き算
class SubExpression(OperatorExpression):
    def __init__(self, left, right):
        self.node = OperatorExpression(left, right)

    def interpret(self, context=None):
        return self.node.left.interpret() - self.node.right.interpret()


# 乗算
class MulExpression(OperatorExpression):
    def __init__(self, left, right):
        self.node = OperatorExpression(left, right)

    def interpret(self, context=None):
        return self.node.left.interpret() * self.node.right.interpret()


# 除算
class DivExpression(OperatorExpression):
    def __init__(self, left, right):
        self.node = OperatorExpression(left, right)

    def interpret(self, context=None):
        return self.node.left.interpret() / self.node.right.interpret()


# 中置記法(数式)を後置記法に変換するクラス
class Postfix:
    def is_numeric(self, s):
        try:
            float(s)
        except ValueError:
            return False
        else:
            return True

    # 後置記法の配列を取得
    def get_postfix(self, expr):
        operator_dict = {"*": 30, "/": 30, "+": 20, "-": 20, "(": 10, ")": 10}

        stack = []
        postfix_list = []
        infix_expr = expr.replace(' ', '')
        token_list = []
        temstr = ''
        for i in range(len(infix_expr)):
            char = infix_expr[i]
            is_operator = char in operator_dict.keys()
            if char == '-':
                if i == 0:
                    is_operator = False
                else:
                    if infix_expr[i - 1] == '(':
                        is_operator = False

            if is_operator:
                if len(temstr) != 0:
                    token_list.append(temstr)
                    temstr = ''
                token_list.append(char)
            else:
                temstr = temstr + char
        if len(temstr) != 0:
            token_list.append(temstr)

        for token in token_list:
            if self.is_numeric(token):
                postfix_list.append(token)
            elif token == "(":
                stack.append(token)
            elif token == ")":
                top_token = stack.pop()
                while top_token != "(":
                    postfix_list.append(top_token)
                    top_token = stack.pop()
            else:
                while len(stack) != 0 and operator_dict[stack[-1]] >= operator_dict[token]:
                    postfix_list.append(stack.pop())
                stack.append(token)
        while len(stack) != 0:
            postfix_list.append(stack.pop())

        return postfix_list


class Calculator:
    def __init__(self):
        self.root_expression = None
        self.cal_success = True
        self.postfix_list = []

    # 二分木を構築
    def get_binary_tree(self, exp):
        p = Postfix()
        self.postfix_list = p.get_postfix(exp)   # 後置記法の配列
        f = lambda x: float(x) if p.is_numeric(x) else x
        post_list = list(map(f, self.postfix_list))
        # 二分木を構築
        expression_stack = []
        operators = '+-*/'
        for postfix in post_list:
            item = str(postfix)
            if item in operators:
                right = expression_stack[-1]
                left = expression_stack[-2]
                expression_stack = expression_stack[0:-2]
                if item == '+':
                    expression_stack.append(PlusExpression(left, right))
                elif item == '-':
                    expression_stack.append(SubExpression(left, right))
                elif item == '*':
                    expression_stack.append(MulExpression(left, right))
                elif item == '/':
                    expression_stack.append(DivExpression(left, right))
            else:
                expression_stack.append(ValueExpression(postfix))

        return expression_stack[0]

    def get_expression_value(self, expression):
        self.cal_success = True
        rtn = 0
        try:
            self.root_expression = self.get_binary_tree(expression)
            rtn = self.root_expression.interpret()
        except Exception as e:
            self.cal_success = False
            print('不正な数式:[{}]  (error:{})'.format(expression, e))

        return rtn


if __name__ == '__main__':
    # ----- Client -----
    exp_list = []
    exp_list.append('((3+5)/(10-8))*(2+3)')
    exp_list.append('5*(-12.5 - 3)')
    exp_list.append('-2*2 - (-9)')
    cal = Calculator()
    for exp in exp_list:
        val = cal.get_expression_value(exp)
        if cal.cal_success:
            print('後置記法の数式:[{}]'.format('  '.join(cal.postfix_list)))
            print('{} = {}'.format(exp, val))

実行結果:

後置記法の数式:[3  5  +  10  8  -  /  2  3  +  *]
((3+5)/(10-8))*(2+3) = 20.0
後置記法の数式:[5  -12.5  3  -  *]
5*(-12.5 - 3) = -77.5
後置記法の数式:[-2  2  *  -9  -]
-2*2 - (-9) = 5.0

Iterator パターン

Iterator.py
# ----------------------------------------------------------------------
#                           Iterator パターン
# ----------------------------------------------------------------------
# 集合の各要素に順次にアクセスする方法を提供します。

from abc import ABCMeta, abstractmethod


class Iterator(metaclass=ABCMeta):
    @abstractmethod
    def has_next(self):
        pass

    @abstractmethod
    def next(self):
        pass


class Student:
    def __init__(self, name):
        self.name = name

    def show_info(self):
        print(self.name)


class StudentIterator(Iterator):
    def __init__(self, students):
        # 学生名を基準に降順でソート
        self.students = sorted(students, key=lambda x: x.name, reverse=True)
        self.index = 0

    def has_next(self):
        if self.students is None:
            return False
        return self.index < len(self.students)

    def next(self):
        stu = self.students[self.index]
        self.index += 1
        return stu


if __name__ == '__main__':
    students = [Student('学生A'), Student('学生C'), Student('学生B')]
    student_iterator = StudentIterator(students)

    while student_iterator.has_next():
        student = student_iterator.next()
        student.show_info()

実行結果:

学生C
学生B
学生A

Mediator パターン

Mediator.py
# ----------------------------------------------------------------------
#                           Mediator パターン
# ----------------------------------------------------------------------
# オブジェクト間の関係を中間オブジェクトで制御します。

from abc import ABCMeta, abstractmethod


class Mediator(metaclass=ABCMeta):
    @abstractmethod
    def send(self, message, colleague):
        pass


class Colleague(metaclass=ABCMeta):
    def __init__(self, mediator):
        self.mediator = mediator

    @abstractmethod
    def send(self, message):
        pass

    @abstractmethod
    def notify(self, message):
        pass


class ConcreteColleagueA(Colleague):

    def send(self, message):
        self.mediator.send(message, self)

    def notify(self, message):
        print('同僚Aが入手した情報:{}'.format(message))


class ConcreteColleagueB(Colleague):
    def send(self, message):
        self.mediator.send(message, self)

    def notify(self, message):
        print('同僚Bが入手した情報:{}'.format(message))


class ConcreteMediator(Mediator):
    def __init__(self):
        self.__colleagueA = None
        self.__colleagueB = None

    @property
    def colleagueA(self):
        return

    @colleagueA.setter
    def colleagueA(self, value):
        self.__colleagueA = value

    @property
    def colleagueB(self):
        return

    @colleagueB.setter
    def colleagueB(self, value):
        self.__colleagueB = value

    def send(self, message, colleague):
        if colleague == self.__colleagueA:
            self.__colleagueB.notify(message)
        else:
            self.__colleagueA.notify(message)


if __name__ == "__main__":
    m = ConcreteMediator()
    a = ConcreteColleagueA(m)
    b = ConcreteColleagueB(m)

    m.colleagueA = a
    m.colleagueB = b

    a.send('運動が好きです。')
    b.send('音楽が好きです。')

実行結果:

同僚Bが入手した情報:運動が好きです。
同僚Aが入手した情報:音楽が好きです。

Memento パターン

Memento.py
# ----------------------------------------------------------------------
#                 Memento パターン
# ----------------------------------------------------------------------
# ある時点のオブジェクトの状態を保存し、オブジェクトを復元できるようにします。

class Originator:
    def __init__(self, state, val):
        self.state = state
        self.val = val

    def save_memento(self):
        return Memento(self.state, self.val)

    def recover_memento(self, memento):
        self.state = memento.state
        self.val = memento.val

    def show_infor(self):
        print('state: {}  val: {}'.format(self.state, self.val))


class Memento:
    def __init__(self, state, val):
        self.state = state
        self.val = val


class Caretaker:
    def __init__(self, memento):
        self.memento = memento


if __name__ == '__main__':
    originator = Originator('状態1', 10)
    originator.show_infor()
    memento_temp = originator.save_memento()
    caretaker = Caretaker(memento_temp)
    originator.state = '状態2'
    originator.val = 60
    originator.show_infor()
    originator.recover_memento(caretaker.memento)
    originator.show_infor()

実行結果:

state: 状態1  val: 10
state: 状態2  val: 60
state: 状態1  val: 10

Observer パターン

Observer.py
# ----------------------------------------------------------------------
#                     Observer パターン
# ----------------------------------------------------------------------
# 状態の変化を複数のオブジェクトに通知します。

from abc import ABCMeta, abstractmethod


# 抽象観察者
class Observer(metaclass=ABCMeta):
    @abstractmethod
    def update(self, notice):
        pass


# 発信者(基底)
class Notice:
    def __init__(self):
        self.observers = []

    def attach(self, obs):
        if obs not in self.observers:
            self.observers.append(obs)

    def detach(self, obs):
        try:
            self.observers.remove(obs)
        except ValueError:
            pass

    def notify(self):
        for obs in self.observers:
            obs.update(self)


# 観察者
class StudentObserver(Observer):
    def __init__(self, name):
        self.name = name
        self.status = ''
        self.notice = None

    def register(self, notice):
        self.notice = notice
        notice.attach(self)

    def unregister(self):
        if self.notice:
            self.notice.detach(self)
            self.notice = None

    def update(self, notice):
        self.status = notice.status

    def show_status(self):
        print('{}:{}'.format(self.name, self.status))


# 発信者
class NoticeCenter(Notice):
    def __init__(self):
        super().__init__()
        self.__status = ''

    @property
    def status(self):
        return self.__status

    @status.setter
    def status(self, status):
        self.__status = status
        self.notify()


if __name__ == '__main__':
    notice_center = NoticeCenter()
    stu1 = StudentObserver('学生A')
    stu1.register(notice_center)
    stu2 = StudentObserver('学生B')
    stu2.register(notice_center)

    notice_center.status = '休憩開始'
    stu1.show_status()
    stu2.show_status()

    notice_center.status = '休憩終了'
    stu1.show_status()
    stu2.show_status()

実行結果:

学生A:休憩開始
学生B:休憩開始
学生A:休憩終了
学生B:休憩終了

State パターン

State.py
# ----------------------------------------------------------------------
#                           State パターン
# ----------------------------------------------------------------------
# 状態をクラスとして表現します。(クラスを切り替えることで「状態の変化」を表します)
from abc import ABCMeta, abstractmethod


# 抽象状態
class State(metaclass=ABCMeta):
    @abstractmethod
    def heat(self):
        pass

    @abstractmethod
    def cool(self):
        pass


# 液体状態を表すクラス
class Water(State):
    def heat(self):
        return Vapour()

    def cool(self):
        return Ice()

    def __str__(self):
        return '液体状態の水'


# 気体状態を表すクラス
class Vapour(State):
    def heat(self):
        return self

    def cool(self):
        return Water()

    def __str__(self):
        return '気体状態の水'


# 個体状態を表すクラス
class Ice(State):
    def heat(self):
        return Water()

    def cool(self):
        return self

    def __str__(self):
        return '個体状態の水'


class Context:
    def __init__(self, state):
        self.state = state

    def heat(self):
        self.state = self.state.heat()

    def cool(self):
        self.state = self.state.cool()

    def set_state(self, state):
        self.state = state


if __name__ == '__main__':
    context = Context(Ice())  # 初期:個体状態
    for i in range(0, 2):
        pre_state = format(context.state)
        context.heat()
        print('{} → 加熱後 → {}'.format(pre_state, context.state))
    print('-' * 35)
    for i in range(0, 3):
        pre_state = format(context.state)
        context.cool()
        print('{} → 冷却後 → {}'.format(pre_state, context.state))

実行結果:

個体状態の水 → 加熱後 → 液体状態の水
液体状態の水 → 加熱後 → 気体状態の水
-----------------------------------
気体状態の水 → 冷却後 → 液体状態の水
液体状態の水 → 冷却後 → 個体状態の水
個体状態の水 → 冷却後 → 個体状態の水

Strategy パターン

Strategy.py
# ----------------------------------------------------------------------
#                           Strategy パターン
# ----------------------------------------------------------------------
# 各種のアルゴリズムを定義し、相互に置き換えられるようにします。
from abc import ABCMeta, abstractmethod


class Strategy(metaclass=ABCMeta):
    @abstractmethod
    def run(self):
        pass


class StrategyAlgorithm1(Strategy):
    def run(self):
        print('アルゴリズム1での処理')


class StrategyAlgorithm2(Strategy):
    def run(self):
        print('アルゴリズム2での処理')


class Context:
    def __init__(self, strategy):
        self.strategy = strategy

    def set_strategy(self, strategy):
        self.strategy = strategy

    def do_strategy(self):
        self.strategy.run()


if __name__ == '__main__':
    c = Context(StrategyAlgorithm1())
    c.do_strategy()
    c.set_strategy(StrategyAlgorithm2())
    c.do_strategy()

実行結果:

アルゴリズム1での処理
アルゴリズム2での処理

Template Method パターン

TemplateMethod.py
# ----------------------------------------------------------------------
#                 Template Method パターン
# ----------------------------------------------------------------------
# 抽象クラスで処理の枠組みを定め、サブクラスでその具体的な内容を実装します。
from abc import ABCMeta, abstractmethod


class Template(metaclass=ABCMeta):

    @abstractmethod
    def method1(self):
        pass

    @abstractmethod
    def method2(self):
        pass

    def start_common(self):
        print('Template:start_common')

    def stop_common(self):
        print('Template:stop_common')

    def run(self):
        self.start_common()
        self.method1()
        self.method2()
        self.stop_common()


class SubClassA(Template):
    def method1(self):
        print('SubClassA:method1')

    def method2(self):
        print('SubClassA:method2')


class SubClassB(Template):
    def method1(self):
        print('SubClassB:method1')

    def method2(self):
        print('SubClassB:method2')


if __name__ == '__main__':
    s_a = SubClassA()
    s_b = SubClassB()
    s_a.run()
    print('--------------------')
    s_b.run()

実行結果:

Template:start_common
SubClassA:method1
SubClassA:method2
Template:stop_common
--------------------
Template:start_common
SubClassB:method1
SubClassB:method2
Template:stop_common

Visitor パターン

Visitor.py
# ----------------------------------------------------------------------
#                           Visitor パターン
# ----------------------------------------------------------------------
# 機能をビジターに記述することで、処理の追加を簡単にします。

from abc import ABCMeta, abstractmethod


class AnimalElement(metaclass=ABCMeta):
    @abstractmethod
    def accept(self, visitor):
        pass


class Visitor(metaclass=ABCMeta):
    @abstractmethod
    def visit_cat(self, cat_element):
        pass

    def visit_dog(self, dog_element):
        pass


class CatElement(AnimalElement):
    def accept(self, visitor):
        visitor.visit_cat(self)


class DogElement(AnimalElement):
    def accept(self, visitor):
        visitor.visit_dog(self)


class OwnerVisitor(Visitor):
    def visit_cat(self, cat_element):
        print('visit_cat in OwnerVisitor')
        print('Ownerビジターの処理:猫に餌をやるアクション')

    def visit_dog(self, dog_element):
        print('visit_dog in OwnerVisitor')
        print('OwnerVisitorの処理:犬に餌をやるアクション')


class ChildVisitor(Visitor):
    def visit_cat(self, cat_element):
        print('visit_cat in ChildVisitor')
        print('ChildVisitorの処理:猫に餌をやるアクション')

    def visit_dog(self, dog_element):
        print('visit_dog in ChildVisitor')
        print('ChildVisitorの処理:犬に餌をやるアクション')


class Home(list):
    def act(self, visitor):
        for e in self:
            e.accept(visitor)


if __name__ == '__main__':
    home = Home()
    home.append(CatElement())
    home.append(DogElement())
    home.act(OwnerVisitor())
    print('-' * 50)
    home.act(ChildVisitor())

実行結果:

visit_cat in OwnerVisitor
Ownerビジターの処理:猫に餌をやるアクション
visit_dog in OwnerVisitor
OwnerVisitorの処理:犬に餌をやるアクション
--------------------------------------------------
visit_cat in ChildVisitor
ChildVisitorの処理:猫に餌をやるアクション
visit_dog in ChildVisitor
ChildVisitorの処理:犬に餌をやるアクション

その他のデザインパターン

Pythonで生成に関するデザインパターンを作成してみた
Pythonで構造に関するデザインパターンを作成してみた


1
0
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
1
0