6-5
ストラテジーで型チェック分岐を排除
せっかく種類ごとにクラスを分けたのに、呼び出し側で isinstance による型チェック分岐を書いてしまうと、分岐地獄が場所を変えて再発しただけになる。種類によって変わる計算は抽象メソッドとして宣言し、各クラスに実装させれば、呼び出し側は型を一切意識せず同じメソッドを呼ぶだけで済む。
証券口座ごとの譲渡益課税
✕ Bad
def gain_tax(account: Account, gain: Decimal) -> Decimal:
if isinstance(account, NisaAccount):
return Decimal("0")
elif isinstance(account, SpecificAccount):
return (gain * Decimal("0.20315")).quantize(Decimal("1"))
# 法人口座を追加したら、この種の分岐を全部探して直す羽目になる
raise ValueError("unknown account type")✓ Good
class Account(ABC):
@abstractmethod
def gain_tax(self, gain: Decimal) -> Decimal: ...
class NisaAccount(Account):
def gain_tax(self, gain: Decimal) -> Decimal:
return Decimal("0") # 非課税
class SpecificAccount(Account):
TAX_RATE = Decimal("0.20315")
def gain_tax(self, gain: Decimal) -> Decimal:
return (gain * self.TAX_RATE).quantize(Decimal("1"))
# 呼び出し側はどの口座でも同じ1行
tax = account.gain_tax(gain)ABC の @abstractmethod は「実装し忘れたクラスはインスタンス化できない」という強制力を持つ。新しい口座種別(法人口座など)を追加するとき、gain_tax を書かなければその場でエラーになるので、isinstance 分岐の修正漏れのような静かなバグが構造的に起きない。