6-4
条件をポリシーとして合成する
「優良取引先なら与信枠を増やす」のような判定は、複数の条件の組み合わせでできている。判定メソッドの中に条件をベタ書きすると、似たランク判定(優良・標準など)の間で同じ条件が重複し、基準変更のたびに全メソッドを探して直すことになる。条件を1つずつ独立したルールとして切り出し、ルールの組み合わせ(ポリシー)としてランクを表現すれば、条件の再利用と差し替えが自在になる。
取引先の与信ランク判定
✕ Bad
def is_prime_partner(history: TradeHistory) -> bool:
if history.years_of_trade >= 3:
if history.annual_amount >= 5_000_000:
if history.late_payment_rate <= 0.01:
return True
return False
def is_standard_partner(history: TradeHistory) -> bool:
if history.annual_amount >= 1_000_000:
if history.late_payment_rate <= 0.01: # 同じ条件のコピー
return True
return False✓ Good
CreditRule = Callable[[TradeHistory], bool]
def long_term_trade(h: TradeHistory) -> bool:
return h.years_of_trade >= 3
def large_annual_amount(h: TradeHistory) -> bool:
return h.annual_amount >= 5_000_000
def low_late_payment(h: TradeHistory) -> bool:
return h.late_payment_rate <= 0.01
@dataclass(frozen=True)
class CreditPolicy:
rules: tuple[CreditRule, ...]
def comply_with_all(self, history: TradeHistory) -> bool:
return all(rule(history) for rule in self.rules)
PRIME = CreditPolicy((long_term_trade, large_annual_amount, low_late_payment))
STANDARD = CreditPolicy((large_annual_amount, low_late_payment))原典の言語ではルールを interface+クラスで表すが、Python は関数が第一級なので関数そのものをルールにできる。「遅延率の基準を 0.5% に厳格化」は low_late_payment の1か所を直せば全ポリシーに波及し、新ランクの追加もルールの組み合わせを1行書くだけになる。