switch 重複を Enum+多態で一元化
「種類ごとに値や振る舞いが変わる」を関数ごとの if-elif で書くと、同じ分岐が何か所にもコピーされる。新しい種類を追加するとき全箇所を漏れなく直す必要があり、1か所でも忘れると静かに間違った値を返す。種類を Enum にし、種類ごとの値と振る舞いを1か所にまとめれば、追加・変更の影響範囲が一点に閉じる。
01 消費税区分ごとの税率・請求書表記
def tax_rate(category: str) -> Decimal:
if category == "standard":
return Decimal("0.10")
elif category == "reduced":
return Decimal("0.08")
elif category == "exempt":
return Decimal("0")
return Decimal("0")
def invoice_label(category: str) -> str:
if category == "standard":
return "10%対象"
elif category == "reduced":
return "8%対象(軽減税率)"
# "exempt" の追加を忘れても、空文字が返るだけで気づけない
return ""class TaxCategory(Enum):
STANDARD = auto()
REDUCED = auto()
EXEMPT = auto()
@dataclass(frozen=True)
class TaxRule:
rate: Decimal
invoice_label: str
TAX_RULES: dict[TaxCategory, TaxRule] = {
TaxCategory.STANDARD: TaxRule(Decimal("0.10"), "10%対象"),
TaxCategory.REDUCED: TaxRule(Decimal("0.08"), "8%対象(軽減税率)"),
TaxCategory.EXEMPT: TaxRule(Decimal("0"), "非課税"),
}
def tax_rule(category: TaxCategory) -> TaxRule:
return TAX_RULES[category]区分を str ではなく Enum にすると、タイポは即エラーになり、登録漏れも assert set(TAX_RULES) == set(TaxCategory) のテスト1本で検出できる。
02 区分ごとに計算ロジックが違うなら多態に
class WithholdingTax(ABC):
@abstractmethod
def amount(self, fee: Decimal) -> Decimal: ...
class LectureFeeTax(WithholdingTax):
def amount(self, fee: Decimal) -> Decimal:
# 講演料: 100万円以下は10.21%、超過分は20.42%
if fee <= 1_000_000:
return (fee * Decimal("0.1021")).quantize(Decimal("1"))
over = fee - 1_000_000
return (Decimal("102100") + over * Decimal("0.2042")).quantize(Decimal("1"))
class DesignFeeTax(WithholdingTax):
def amount(self, fee: Decimal) -> Decimal:
return (fee * Decimal("0.1021")).quantize(Decimal("1"))Java の interface に相当するものは Python では ABC(または Protocol)。呼び出し側は tax.amount(fee) と書くだけで、報酬の種類ごとの計算式はそれぞれのクラスの中に閉じる。新しい報酬種別の追加は「クラスを1つ書く」ことであり、既存の分岐をいじる作業ではなくなる。