リファクタリング
14-3

条件を読みやすく抽出する

not や比較演算子が生のまま並んだ条件式は、読むたびに頭の中で「アクティブでない、つまり利用停止…」と翻訳させられる。条件式を目的を表す名前のメソッドに抽出し、データを持っているクラス側に置けば、ガード節が仕様書の文章のように読めるようになる。

否定演算子と比較式をそのまま読ませる貸出判定

Bad

not in_stock や len(...) >= LOAN_LIMIT の解読を、読み手全員に毎回強いている

生の条件式が並ぶガード節
def register_loan(member: Member, book: Book) -> LoanRecord:
    if not member.is_active:
        raise LoanError("利用停止中の会員です")
    if not book.in_stock:
        raise LoanError("この蔵書は貸出中です")
    if len(member.loans) >= LOAN_LIMIT:
        raise LoanError("同時貸出の上限に達しています")
    ...
Good

条件式を目的の名前で抽出し、Member / Book 自身に答えさせる。判定ルールの変更も持ち主のクラスで完結する

目的を表す述語メソッドに抽出
class Member:
    @property
    def is_suspended(self) -> bool:
        return not self.is_active

    def has_reached_loan_limit(self) -> bool:
        return len(self.loans) >= LOAN_LIMIT


def register_loan(member: Member, book: Book) -> LoanRecord:
    if member.is_suspended:
        raise LoanError("利用停止中の会員です")
    if book.is_on_loan:
        raise LoanError("この蔵書は貸出中です")
    if member.has_reached_loan_limit():
        raise LoanError("同時貸出の上限に達しています")

    return LoanRecord(member_id=member.id, book_id=book.id,
                      due_date=date.today() + LOAN_PERIOD)

抽出した名前は実装の説明(not is_active)ではなく業務の語彙(利用停止中か?)にする。Python では引数のない単純な判定は @property、引数や計算を伴う判定は通常のメソッドにすると自然。条件の持ち主にロジックを寄せる点で、低凝集の解消(第5章・第8章)にもつながる。

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

条件を読みやすく抽出する

not や比較演算子が生のまま並んだ条件式は、読むたびに頭の中で「アクティブでない、つまり利用停止…」と翻訳させられる。条件式を目的を表す名前のメソッドに抽出し、データを持っているクラス側に置けば、ガード節が仕様書の文章のように読めるようになる。

否定演算子と比較式をそのまま読ませる貸出判定

Bad

not in_stock や len(...) >= LOAN_LIMIT の解読を、読み手全員に毎回強いている

生の条件式が並ぶガード節
def register_loan(member: Member, book: Book) -> LoanRecord:
    if not member.is_active:
        raise LoanError("利用停止中の会員です")
    if not book.in_stock:
        raise LoanError("この蔵書は貸出中です")
    if len(member.loans) >= LOAN_LIMIT:
        raise LoanError("同時貸出の上限に達しています")
    ...
Good

条件式を目的の名前で抽出し、Member / Book 自身に答えさせる。判定ルールの変更も持ち主のクラスで完結する

目的を表す述語メソッドに抽出
class Member:
    @property
    def is_suspended(self) -> bool:
        return not self.is_active

    def has_reached_loan_limit(self) -> bool:
        return len(self.loans) >= LOAN_LIMIT


def register_loan(member: Member, book: Book) -> LoanRecord:
    if member.is_suspended:
        raise LoanError("利用停止中の会員です")
    if book.is_on_loan:
        raise LoanError("この蔵書は貸出中です")
    if member.has_reached_loan_limit():
        raise LoanError("同時貸出の上限に達しています")

    return LoanRecord(member_id=member.id, book_id=book.id,
                      due_date=date.today() + LOAN_PERIOD)

抽出した名前は実装の説明(not is_active)ではなく業務の語彙(利用停止中か?)にする。Python では引数のない単純な判定は @property、引数や計算を伴う判定は通常のメソッドにすると自然。条件の持ち主にロジックを寄せる点で、低凝集の解消(第5章・第8章)にもつながる。

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