動機

「関数の抽出」はきわめて頻繁に行われるリファクタリング。
いつ独立した関数として取り出すかというは「2回以上使われるコードはそれ自体を関数にすべき」のように、再利用に基づいている。
しかし、最も納得できるのは意図と実装の分離です。何をしているか調べなければわからないコードの断片があるとしたら、「何」をしているかを示す名前の関数として抽出すべきです。そうすれば関数を読み返すだけで関数の目的がすぐに伝わってきます。

手順

  • 新たな関数を作り、その意図に沿って命名する(どうやるかではなく、何をするかによって名づける。)
  • 抽出したいコードを、元の関数から新たな関数にコピーする。
  • 抽出したコードを調べて、元の関数ではスコープ内だったが抽出後の関数ではスコープ外になった変数を特定する。それらをパラメータとして渡す。
  • 全ての変数を処置したらコンパイルする。
  • 元の関数に残った抽出前のコードを、抽出された関数への呼び出しに置き換える。
  • テストする。
  • 残りのコードを見て、抽出したコードと同じまたは類似したコードを探し、「関数呼び出しによるインラインコードの置き換え」を適用し、新しい関数を呼ぶ形にできないか検討する。

Before

from enum import Enum


class Category(Enum):
    A = 1
    B = 2
    C = 3


def calculate_tax(category: Category, income: int) -> int:
    if category == Category.A:
        discount = 10
    elif category == Category.B:
        discount = 5
    else:
        discount = 0
    return income * (100 - discount) // 100

After

from enum import Enum


class Category(Enum):
    A = 1
    B = 2
    C = 3


def calculate_tax(category: Category, income: int) -> int:
    discount = calc_discount(category)
    return income * (100 - discount) // 100


def calc_discount(category: Category) -> int:
    if category == Category.A:
        discount = 10
    elif category == Category.B:
        discount = 5
    else:
        discount = 0
    return discount