設計の初歩
2-3

意味のまとまりでメソッド化

計算ロジックをベタ書きで並べると、処理の切れ目をコメントで区切りたくなる。コメントで区切りたくなったらそこが関数抽出のサインだ。意味のまとまりごとに関数へ切り出して名前を付ければ、メインの処理は「何を順にやるか」だけを語る目次になり、各ステップは単体でテストできる。

請求額計算のステップを関数に分ける

Bad

合計・消費税・源泉徴収・前受金充当という別々の関心事が1つの関数に詰め込まれている

コメントで区切られた一枚岩の関数
def calc_billing_amount(
    advisory_fee: int,
    bookkeeping_fee: int,
    year_end_fee: int,
    advance_received: int,
) -> int:
    # 役務の税抜合計
    subtotal = advisory_fee + bookkeeping_fee + year_end_fee
    # 消費税を加算
    total_with_tax = subtotal + subtotal * 10 // 100
    # 源泉所得税(報酬の10.21%)を控除
    after_withholding = total_with_tax - subtotal * 1021 // 10000
    # 前受金を充当
    return max(after_withholding - advance_received, 0)
Good

ステップごとに純粋関数へ抽出。メイン関数は計算の流れを語る目次になる

意味単位の関数に分割
def sum_service_fees(advisory: int, bookkeeping: int, year_end: int) -> int:
    return advisory + bookkeeping + year_end

def add_consumption_tax(subtotal: int) -> int:
    return subtotal + subtotal * 10 // 100

def deduct_withholding_tax(amount: int, subtotal: int) -> int:
    return amount - subtotal * 1021 // 10000

def offset_advance_received(amount: int, advance_received: int) -> int:
    return max(amount - advance_received, 0)

def calc_billing_amount(
    advisory_fee: int,
    bookkeeping_fee: int,
    year_end_fee: int,
    advance_received: int,
) -> int:
    subtotal = sum_service_fees(advisory_fee, bookkeeping_fee, year_end_fee)
    with_tax = add_consumption_tax(subtotal)
    after_withholding = deduct_withholding_tax(with_tax, subtotal)
    return offset_advance_received(after_withholding, advance_received)

源泉徴収率の改定や税率変更があっても、直す関数は1つだけ。各関数は引数 → 戻り値だけで完結する純粋関数なので、境界値のテストも書きやすい。

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

意味のまとまりでメソッド化

計算ロジックをベタ書きで並べると、処理の切れ目をコメントで区切りたくなる。コメントで区切りたくなったらそこが関数抽出のサインだ。意味のまとまりごとに関数へ切り出して名前を付ければ、メインの処理は「何を順にやるか」だけを語る目次になり、各ステップは単体でテストできる。

請求額計算のステップを関数に分ける

Bad

合計・消費税・源泉徴収・前受金充当という別々の関心事が1つの関数に詰め込まれている

コメントで区切られた一枚岩の関数
def calc_billing_amount(
    advisory_fee: int,
    bookkeeping_fee: int,
    year_end_fee: int,
    advance_received: int,
) -> int:
    # 役務の税抜合計
    subtotal = advisory_fee + bookkeeping_fee + year_end_fee
    # 消費税を加算
    total_with_tax = subtotal + subtotal * 10 // 100
    # 源泉所得税(報酬の10.21%)を控除
    after_withholding = total_with_tax - subtotal * 1021 // 10000
    # 前受金を充当
    return max(after_withholding - advance_received, 0)
Good

ステップごとに純粋関数へ抽出。メイン関数は計算の流れを語る目次になる

意味単位の関数に分割
def sum_service_fees(advisory: int, bookkeeping: int, year_end: int) -> int:
    return advisory + bookkeeping + year_end

def add_consumption_tax(subtotal: int) -> int:
    return subtotal + subtotal * 10 // 100

def deduct_withholding_tax(amount: int, subtotal: int) -> int:
    return amount - subtotal * 1021 // 10000

def offset_advance_received(amount: int, advance_received: int) -> int:
    return max(amount - advance_received, 0)

def calc_billing_amount(
    advisory_fee: int,
    bookkeeping_fee: int,
    year_end_fee: int,
    advance_received: int,
) -> int:
    subtotal = sum_service_fees(advisory_fee, bookkeeping_fee, year_end_fee)
    with_tax = add_consumption_tax(subtotal)
    after_withholding = deduct_withholding_tax(with_tax, subtotal)
    return offset_advance_received(after_withholding, advance_received)

源泉徴収率の改定や税率変更があっても、直す関数は1つだけ。各関数は引数 → 戻り値だけで完結する純粋関数なので、境界値のテストも書きやすい。

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