低凝集
5-2

初期化ロジックは生成側に集める

コンストラクタを公開したままにすると、「どんな値で初期化すべきか」という知識が呼び出し側に散らばる。料金改定のような仕様変更で全呼び出し箇所を探し回ることになる。目的別のファクトリメソッドを用意して、生成の知識をクラス側に集める。

確定申告の基本報酬の初期化

Bad

「個人は88,000円」という業務知識が、申込画面と見積画面の両方に直書きされている

初期化の知識が呼び出し側に分散
# 申込フォーム側のモジュール
def create_application(client_name: str) -> Application:
    fee = TaxReturnFee(88_000)  # 個人向け基本報酬…のつもり
    return Application(client_name, fee)


# 見積画面側のモジュール(別ファイル)
def build_estimate(client_name: str) -> Estimate:
    fee = TaxReturnFee(88_000)  # 同じ知識の重複。料金改定で片方だけ直し忘れる
    return Estimate(client_name, fee)
Good

目的別のファクトリメソッドに生成知識を集約。改定は定数1か所の修正で済む

classmethod ファクトリで生成を一元化
from dataclasses import dataclass
from typing import ClassVar


@dataclass(frozen=True)
class TaxReturnFee:
    amount: int

    _INDIVIDUAL: ClassVar[int] = 88_000
    _CORPORATION: ClassVar[int] = 165_000

    def __post_init__(self) -> None:
        if self.amount <= 0:
            raise ValueError("報酬額は正の値にしてください")

    @classmethod
    def for_individual(cls) -> "TaxReturnFee":
        return cls(cls._INDIVIDUAL)

    @classmethod
    def for_corporation(cls) -> "TaxReturnFee":
        return cls(cls._CORPORATION)

Python にはコンストラクタを private にする仕組みがないので、「生成はファクトリメソッドから」という規約を @classmethod と _ 付き定数で表現する。生成パターンが増えすぎてきたら、専用のファクトリクラスへの分離を検討する。

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

初期化ロジックは生成側に集める

コンストラクタを公開したままにすると、「どんな値で初期化すべきか」という知識が呼び出し側に散らばる。料金改定のような仕様変更で全呼び出し箇所を探し回ることになる。目的別のファクトリメソッドを用意して、生成の知識をクラス側に集める。

確定申告の基本報酬の初期化

Bad

「個人は88,000円」という業務知識が、申込画面と見積画面の両方に直書きされている

初期化の知識が呼び出し側に分散
# 申込フォーム側のモジュール
def create_application(client_name: str) -> Application:
    fee = TaxReturnFee(88_000)  # 個人向け基本報酬…のつもり
    return Application(client_name, fee)


# 見積画面側のモジュール(別ファイル)
def build_estimate(client_name: str) -> Estimate:
    fee = TaxReturnFee(88_000)  # 同じ知識の重複。料金改定で片方だけ直し忘れる
    return Estimate(client_name, fee)
Good

目的別のファクトリメソッドに生成知識を集約。改定は定数1か所の修正で済む

classmethod ファクトリで生成を一元化
from dataclasses import dataclass
from typing import ClassVar


@dataclass(frozen=True)
class TaxReturnFee:
    amount: int

    _INDIVIDUAL: ClassVar[int] = 88_000
    _CORPORATION: ClassVar[int] = 165_000

    def __post_init__(self) -> None:
        if self.amount <= 0:
            raise ValueError("報酬額は正の値にしてください")

    @classmethod
    def for_individual(cls) -> "TaxReturnFee":
        return cls(cls._INDIVIDUAL)

    @classmethod
    def for_corporation(cls) -> "TaxReturnFee":
        return cls(cls._CORPORATION)

Python にはコンストラクタを private にする仕組みがないので、「生成はファクトリメソッドから」という規約を @classmethod と _ 付き定数で表現する。生成パターンが増えすぎてきたら、専用のファクトリクラスへの分離を検討する。

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