メソッド設計
12-1

自分のデータは自分で操作する

メソッドは原則として、自分のクラスが持つデータを操作するために存在する。よそのクラスの属性を外から読み書きして計算する構造にすると、データとロジックが離れて低凝集になり、同じ計算とチェックがあちこちに散らばる。データを持つクラス自身に操作メソッドを実装すれば、不正な状態への変更をその場で防げる。

請求書の入金消込

Bad

別クラスが請求書の残高を外から読み取り、計算して書き戻している。残高チェックの責任が呼び出し側に漏れている

よそのクラスのデータを外から書き換える
class Invoice:
    def __init__(self, total: int) -> None:
        self.total = total
        self.balance = total  # 誰でも書き換えられる


class PaymentRegistrar:
    def register(self, invoice: Invoice, paid: int) -> None:
        # 残高の計算とチェックが Invoice の外にある
        new_balance = invoice.balance - paid
        if new_balance < 0:
            new_balance = 0  # 過入金の扱いを呼び出し側が勝手に決めている
        invoice.balance = new_balance
Good

残高を持つ Invoice 自身に消込メソッドを実装する。不正な入金はメソッドの入口で遮断される

データを持つクラスに操作を寄せる
class Invoice:
    def __init__(self, total: int) -> None:
        if total < 0:
            raise ValueError("請求額が負です")
        self._total = total
        self._balance = total

    @property
    def balance(self) -> int:
        return self._balance

    def apply_payment(self, paid: int) -> None:
        if paid <= 0:
            raise ValueError("入金額は正の値で指定してください")
        if paid > self._balance:
            raise ValueError("入金額が残高を超えています")
        self._balance -= paid

Python に private 強制はないが、アンダースコア接頭辞+ @property で「読み取りは公開、変更は自分のメソッド経由のみ」という意図を表現できる。コンストラクタとメソッド入口のガードを揃えれば、Invoice は常に正しい状態しか持てなくなる。

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

自分のデータは自分で操作する

メソッドは原則として、自分のクラスが持つデータを操作するために存在する。よそのクラスの属性を外から読み書きして計算する構造にすると、データとロジックが離れて低凝集になり、同じ計算とチェックがあちこちに散らばる。データを持つクラス自身に操作メソッドを実装すれば、不正な状態への変更をその場で防げる。

請求書の入金消込

Bad

別クラスが請求書の残高を外から読み取り、計算して書き戻している。残高チェックの責任が呼び出し側に漏れている

よそのクラスのデータを外から書き換える
class Invoice:
    def __init__(self, total: int) -> None:
        self.total = total
        self.balance = total  # 誰でも書き換えられる


class PaymentRegistrar:
    def register(self, invoice: Invoice, paid: int) -> None:
        # 残高の計算とチェックが Invoice の外にある
        new_balance = invoice.balance - paid
        if new_balance < 0:
            new_balance = 0  # 過入金の扱いを呼び出し側が勝手に決めている
        invoice.balance = new_balance
Good

残高を持つ Invoice 自身に消込メソッドを実装する。不正な入金はメソッドの入口で遮断される

データを持つクラスに操作を寄せる
class Invoice:
    def __init__(self, total: int) -> None:
        if total < 0:
            raise ValueError("請求額が負です")
        self._total = total
        self._balance = total

    @property
    def balance(self) -> int:
        return self._balance

    def apply_payment(self, paid: int) -> None:
        if paid <= 0:
            raise ValueError("入金額は正の値で指定してください")
        if paid > self._balance:
            raise ValueError("入金額が残高を超えています")
        self._balance -= paid

Python に private 強制はないが、アンダースコア接頭辞+ @property で「読み取りは公開、変更は自分のメソッド経由のみ」という意図を表現できる。コンストラクタとメソッド入口のガードを揃えれば、Invoice は常に正しい状態しか持てなくなる。

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