doc.dev1x.org

判定関数パターン

1. 目的

複数の条件変数から構成される判定ロジックを簡潔に管理したい

2. 課題

if文がネストしたコードはメンテナンスが困難

# 3つの因子の組み合わせ毎に異なる値を返す関数
def example(value1, value2, value3):
    if value1 == 'xxx':
        if value2 == 'xxx':
            if value3 == 'xxx':
                return "Pattern 8"
            else:
                return "Pattern 4"
        else:
            if value3 == 'xxx':
                return "Pattern 6"
            else:
                return "Pattern 2"
    else:
        if value2 == 'xxx':
            if value3 == 'xxx':
                return "Pattern 7"
            else:
                return "Pattern 3"
        else:
            if value3 == 'xxx':
                return "Pattern 5"
            else:
                return "Pattern 1"

if __name__=='__main__':
    print(example("---", "---", "---"))     # <= Pattern 1
    print(example("xxx", "---", "---"))     # <= Pattern 2
    print(example("---", "xxx", "---"))     # <= Pattern 3
    print(example("xxx", "xxx", "---"))     # <= Pattern 4
    print(example("---", "---", "xxx"))     # <= Pattern 5
    print(example("xxx", "---", "xxx"))     # <= Pattern 6
    print(example("---", "xxx", "xxx"))     # <= Pattern 7
    print(example("xxx", "xxx", "xxx"))     # <= Pattern 8

3. 解決策

判定処理は判定処理のみを行う関数に分離する

# 因子1を判定する関数
def factor1(value):
    return value == 'xxx'

# 因子2を判定する関数
def factor2(value):
    return value == 'xxx'

# 因子3を判定する関数
def factor3(value):
    return value == 'xxx'

def judge(value1, value2, value3):
    result1 = factor1(value1)
    result2 = factor2(value2)
    result3 = factor3(value3)

    if all([not result1, not result2, not result3]):
        return "Pattern 1"

    elif all([result1, not result2, not result3]):
        return "Pattern 2"

    elif all([not result1, result2, not result3]):
        return "Pattern 3"

    elif all([result1, result2, not result3]):
        return "Pattern 4"

    elif all([not result1, not result2, result3]):
        return "Pattern 5"

    elif all([result1, not result2, result3]):
        return "Pattern 6"

    elif all([not result1, result2, result3]):
        return "Pattern 7"

    elif all([result1, result2, result3]):
        return "Pattern 8"

    else:
        raise ValueError()

    # ↓こういう書き方もありと言えばあり
    # if all([not result1, not result2, not result3]):    return "Pattern 1"
    # if all([result1, not result2, not result3]):        return "Pattern 2"
    # if all([not result1, result2, not result3]):        return "Pattern 3"
    # if all([result1, result2, not result3]):            return "Pattern 4"
    # if all([not result1, not result2, result3]):        return "Pattern 5"
    # if all([result1, not result2, result3]):            return "Pattern 6"
    # if all([not result1, result2, result3]):            return "Pattern 7"
    # if all([result1, result2, result3]):                return "Pattern 8"



if __name__=='__main__':
    print(judge("---", "---", "---"))     # <= Pattern 1
    print(judge("xxx", "---", "---"))     # <= Pattern 2
    print(judge("---", "xxx", "---"))     # <= Pattern 3
    print(judge("xxx", "xxx", "---"))     # <= Pattern 4
    print(judge("---", "---", "xxx"))     # <= Pattern 5
    print(judge("xxx", "---", "xxx"))     # <= Pattern 6
    print(judge("---", "xxx", "xxx"))     # <= Pattern 7
    print(judge("xxx", "xxx", "xxx"))     # <= Pattern 8

判断ロジック内でビジネスロジックや入出力ロジックを実行しない

def main():
    # (1) 判断ロジック呼び出し
    result = judge(param1, param2, param3)

    # (2) 判定結果に応じたビジネスロジック実行
    if result == "Pattern 1":
        # Pattern1 のビジネスロジック実行
        ...

    if result == "Pattern 2":
        # Pattern2 のビジネスロジック実行
        ...
    ...

4. メリット

if文のネストを避けることができる

判断ロジックをビジネスロジックから分離できる

ディシジョンテーブルをそのままプログラムに落とし込める

5. デメリット

因子が増えるとコードが長くなりがち

6. 注意

関連文書

参考資料

Appendix-1 クラスによる実装

class Judgment:

    def factor1(self, factor):
        return factor == 'xxx'

    def factor2(self, factor):
        return factor == 'xxx'

    def factor3(self, factor):
        return factor == 'xxx'

    def judge(self, value1, value2, value3):

        result1 = self.factor1(value1)
        result2 = self.factor2(value2)
        result3 = self.factor3(value3)

        if all([not result1, not result2, not result3]):
            return "Pattern 1"

        elif all([result1, not result2, not result3]):
            return "Pattern 2"

        elif all([not result1, result2, not result3]):
            return "Pattern 3"

        elif all([result1, result2, not result3]):
            return "Pattern 4"

        elif all([not result1, not result2, result3]):
            return "Pattern 5"

        elif all([result1, not result2, result3]):
            return "Pattern 6"

        elif all([not result1, result2, result3]):
            return "Pattern 7"

        elif all([result1, result2, result3]):
            return "Pattern 8"

        else:
            raise ValueError()


if __name__=='__main__':
    j = Judgment()
    print(j.judge("---", "---", "---"))     # <= Pattern 1
    print(j.judge("xxx", "---", "---"))     # <= Pattern 2
    print(j.judge("---", "xxx", "---"))     # <= Pattern 3
    print(j.judge("xxx", "xxx", "---"))     # <= Pattern 4
    print(j.judge("---", "---", "xxx"))     # <= Pattern 5
    print(j.judge("xxx", "---", "xxx"))     # <= Pattern 6
    print(j.judge("---", "xxx", "xxx"))     # <= Pattern 7
    print(j.judge("xxx", "xxx", "xxx"))     # <= Pattern 8