名前設計
10-1

目的駆動で名前を設計する

「金額」「値」のような大雑把な名前は何でも受け入れてしまうため、無関係なロジックを引き寄せて低凝集になる。どんなビジネス目的に使う値なのかを分析し、目的に特化した狭い名前を選べば、置けるロジックが名前によって限定され、構造が崩れにくくなる。

汎用の「金額」クラスにすべてを背負わせる

Bad

顧問料も源泉税も振込手数料もぜんぶ Amount。何の金額かはコメントだけが頼りで、引数を取り違えても型は通る

大雑把な名前が取り違えを許す
class Amount:
    """金額。請求でも税でも手数料でもこれを使う"""
    def __init__(self, value: int) -> None:
        self.value = value


def build_invoice(fee: Amount, rate: float) -> dict:
    tax = Amount(int(fee.value * rate))      # これは源泉徴収税額のつもり
    transfer_fee = Amount(660)               # これは振込手数料のつもり
    paid = Amount(fee.value - tax.value - transfer_fee.value)
    return {"fee": fee, "tax": tax, "paid": paid}
Good

目的に特化した型を用意すれば、源泉税の計算ロジックの置き場所は WithholdingTax 一択になる

目的特化の狭い名前
@dataclass(frozen=True)
class AdvisoryFee:
    """税理士顧問料(税抜)"""
    amount: int


@dataclass(frozen=True)
class WithholdingTax:
    """源泉徴収税額"""
    amount: int

    def __post_init__(self) -> None:
        if self.amount < 0:
            raise ValueError("税額は0以上")

    @classmethod
    def on_advisory_fee(cls, fee: AdvisoryFee, rate: WithholdingRate) -> "WithholdingTax":
        return cls(int(fee.amount * rate.value))

名前を狭くすると「このロジックはどこに書くべきか」「仕様変更時にどこを見ればよいか」が名前から決まる。Python では型ヒント+mypy が AdvisoryFee と WithholdingTax の取り違えを機械的に検出してくれるのも実利。

参考: 『良いコード/悪いコードで学ぶ設計入門』(ミノ駆動 著、技術評論社)第10章。コード例は原則を自分の題材で表現し直したオリジナル。
10-1

目的駆動で名前を設計する

「金額」「値」のような大雑把な名前は何でも受け入れてしまうため、無関係なロジックを引き寄せて低凝集になる。どんなビジネス目的に使う値なのかを分析し、目的に特化した狭い名前を選べば、置けるロジックが名前によって限定され、構造が崩れにくくなる。

汎用の「金額」クラスにすべてを背負わせる

Bad

顧問料も源泉税も振込手数料もぜんぶ Amount。何の金額かはコメントだけが頼りで、引数を取り違えても型は通る

大雑把な名前が取り違えを許す
class Amount:
    """金額。請求でも税でも手数料でもこれを使う"""
    def __init__(self, value: int) -> None:
        self.value = value


def build_invoice(fee: Amount, rate: float) -> dict:
    tax = Amount(int(fee.value * rate))      # これは源泉徴収税額のつもり
    transfer_fee = Amount(660)               # これは振込手数料のつもり
    paid = Amount(fee.value - tax.value - transfer_fee.value)
    return {"fee": fee, "tax": tax, "paid": paid}
Good

目的に特化した型を用意すれば、源泉税の計算ロジックの置き場所は WithholdingTax 一択になる

目的特化の狭い名前
@dataclass(frozen=True)
class AdvisoryFee:
    """税理士顧問料(税抜)"""
    amount: int


@dataclass(frozen=True)
class WithholdingTax:
    """源泉徴収税額"""
    amount: int

    def __post_init__(self) -> None:
        if self.amount < 0:
            raise ValueError("税額は0以上")

    @classmethod
    def on_advisory_fee(cls, fee: AdvisoryFee, rate: WithholdingRate) -> "WithholdingTax":
        return cls(int(fee.amount * rate.value))

名前を狭くすると「このロジックはどこに書くべきか」「仕様変更時にどこを見ればよいか」が名前から決まる。Python では型ヒント+mypy が AdvisoryFee と WithholdingTax の取り違えを機械的に検出してくれるのも実利。

参考: 『良いコード/悪いコードで学ぶ設計入門』(ミノ駆動 著、技術評論社)第10章。コード例は原則を自分の題材で表現し直したオリジナル。