doc.dev1x.org

Strategy

1. 目的

必要に応じてロジックを切り替えたい

2. 課題

条件分岐で処理を分けると保守性が低下する

class Program:
    def execute(self, type):
        if type == 1:
            # 諸々の処理が20-30行ほどある
            ...
            return "わーい"
        elif type == 2:
            # 諸々の処理が20-30行ほどある
            ...
            return "うぇーい"
        else:
            raise ValueError("未定義のtype")

メソッドに分割するとクラスの責務が増える

class Program:
    def execute(self, type):
        if type == 1: reutrn self.logic01()
        if type == 2: reutrn self.logic02()
        raise ValueError("未定義のtype")

    def logic01(self):
        # 諸々の処理が20-30行ほどある
        ...
        return "わーい"

    def logic02(self):
        # 諸々の処理が20-30行ほどある
        ...
        return "うぇーい"

3. 解決策

ロジックを別のクラスとして外部化する

class Program:
    """ 機能クラス """
    def __init__(self, logic):
        self.logic = logic

    def execute(self):
        return self.logic.execute()

class Logic(metaclass=ABCMeta):
    """ ロジッククラスのインターフェイス """
    @abstractmethod
    def execute(self):
        pass

class Logic01(Logic):
    """ ロジッククラスその1 """
    def execute(self):
        # 諸々の処理が20-30行ほどある
        ...
        return "わーい"

class Logic02(Logic):
    """ ロジッククラスその2 """
    def execute(self):
        # 諸々の処理が20-30行ほどある
        ...
        return "うぇーい"

if __name__ == '__main__':
    p1 = Program(Logic01())
    print(p1.execute())     # <= わーい
    p2 = Program(Logic02())
    print(p2.execute())     # <= うぇーい

4. メリット

機能とロジックを分離し、任意に組み合わせることが可能になる

機能クラスとロジッククラスのコード量を減らすことができる

5. デメリット

コード量が増える

6. 注意事項

Factoryと組み合わせる

def factory(type):
    if type == 1: return Program(Logic01())
    if type == 2: return Program(Logic02())
    raise ValueError()

if __name__=='__main__':
    p1 = factory(1)
    print(p1.execute())     # <= わーい
    p2 = factory(2)
    print(p2.execute())     # <= うぇーい

クロージャを使えば更に簡潔に実装できる

class Program:
    """ 機能クラス """
    def __init__(self, logic):
        self.logic = logic

    def execute(self):
        return self.logic()

def logic01():
    return "わーい"

def logic02():
    return "うぇーい"

if __name__ == '__main__':
    p1 = Program(logic01)
    print(p1.execute())     # <= わーい
    p2 = Program(logic02)
    print(p2.execute())     # <= うぇーい

関連

参考資料