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