mcpField
C
code-refactor-master

code-refactor-master

github.com/yennanliu開発14075 回インストール今日更新
リポジトリ

code-refactor-master について

動作を変えずにコード構造を改善したい開発者向けのリファクタリングスキルです。コードスメルや一般的な手法、Java・Python の実践例、テストを伴う段階的な手順を扱います。

主な機能

  1. 01一般的なコードスメルの特定
  2. 02メソッドとクラスのリファクタリング
  3. 03Java 固有のリファクタリング手法
  4. 04Python 固有のリファクタリング手法
  5. 05テストを使った段階的な変更
  6. 06動作を保った性能改善

活用シーン

  1. 01重複コードと複雑さの削減
  2. 02既存コードの保守性向上
  3. 03Java または Python 解法の整理
  4. 04アルゴリズム実装の最適化

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/yennanliu/cs_basics --skill code-refactor-master

インストール後、Claude Code で/code-refactor-masterを入力して読み込まれたか確認します。

詳細

コードリファクタリングマスター

このスキルを使う場面

次のような場合にこのスキルを使います。

  • コードの品質を高めるために既存のコードをリファクタリングする
  • コードの不吉な臭いを取り除く
  • コードの可読性と保守性を向上させる
  • 振る舞いを変えずにパフォーマンスを最適化する
  • デザインパターンを適用する
  • 複雑さを軽減する
  • 技術的負債を解消する

リファクタリングの原則

1. 基本ルール

黄金律: 外部の振る舞いを変えずに、内部構造を改善するコード変更を行います。

重要な原則:

  • 一度にリファクタリングするのは1つだけです
  • リファクタリングのたびにテストを実行します
  • 明確なメッセージを付けてこまめにコミットします
  • リファクタリングと機能開発を分けます
  • API を明示的に変更する場合を除き、後方互換性を維持します

リファクタリングを検討すべき兆候:

  • コードの重複(DRY 原則)
  • 長いメソッド(20~30行超)
  • 大きなクラス(300~500行超)
  • 長いパラメーターリスト(3~4個超)
  • 深くネストした条件分岐(3階層超)
  • コードの内容を説明するコメント(コード自体で内容が明確であるべきです)
2. よくあるコードの不吉な臭い

肥大化:

  • 長いメソッド
  • 大きなクラス
  • プリミティブ型への執着
  • 長いパラメーターリスト
  • データの塊

オブジェクト指向の濫用:

  • switch 文(ポリモーフィズムを検討します)
  • 一時フィールド
  • 拒否された遺産
  • インターフェースが異なる代替クラス

変更の妨げ:

  • 変更の発散(1つのクラスが多くの理由で変更される)
  • ショットガン手術(1つの変更に多くの小さな変更が必要になる)
  • 並列継承階層

不要なもの:

  • コメント(コード自体で内容が明確であるべき箇所)
  • 重複コード
  • 怠惰なクラス
  • デッドコード
  • 投機的一般化

結合:

  • 機能への執着
  • 不適切な親密さ
  • メッセージチェーン
  • 仲介者
3. リファクタリングカタログ

メソッドレベルのリファクタリング:

メソッドの抽出

// Before
void printOwing() {
    printBanner();
    // print details
    System.out.println("name: " + name);
    System.out.println("amount: " + getOutstanding());
}

// After
void printOwing() {
    printBanner();
    printDetails(getOutstanding());
}

void printDetails(double outstanding) {
    System.out.println("name: " + name);
    System.out.println("amount: " + outstanding);
}

メソッドのインライン化

// Before - method too simple
int getRating() {
    return moreThanFiveLateDeliveries() ? 2 : 1;
}

boolean moreThanFiveLateDeliveries() {
    return numberOfLateDeliveries > 5;
}

// After
int getRating() {
    return numberOfLateDeliveries > 5 ? 2 : 1;
}

一時変数をクエリに置き換える

// Before
double calculateTotal() {
    double basePrice = quantity * itemPrice;
    if (basePrice > 1000) {
        return basePrice * 0.95;
    }
    return basePrice * 0.98;
}

// After
double calculateTotal() {
    if (basePrice() > 1000) {
        return basePrice() * 0.95;
    }
    return basePrice() * 0.98;
}

double basePrice() {
    return quantity * itemPrice;
}

変数レベルのリファクタリング:

変数名の変更

# Before
d = 10  # elapsed time in days

# After
elapsed_time_in_days = 10

一時変数の分割

// Before
double temp = 2 * (height + width);
System.out.println(temp);
temp = height * width;
System.out.println(temp);

// After
double perimeter = 2 * (height + width);
System.out.println(perimeter);
double area = height * width;
System.out.println(area);

クラスレベルのリファクタリング:

クラスの抽出

// Before
class Person {
    String name;
    String officeAreaCode;
    String officeNumber;

    String getTelephoneNumber() {
        return "(" + officeAreaCode + ") " + officeNumber;
    }
}

// After
class Person {
    String name;
    TelephoneNumber officeTelephone = new TelephoneNumber();

    String getTelephoneNumber() {
        return officeTelephone.getTelephoneNumber();
    }
}

class TelephoneNumber {
    String areaCode;
    String number;

    String getTelephoneNumber() {
        return "(" + areaCode + ") " + number;
    }
}

条件分岐をポリモーフィズムに置き換える

# Before
class Bird:
    def get_speed(self):
        if self.type == "EUROPEAN":
            return self.get_base_speed()
        elif self.type == "AFRICAN":
            return self.get_base_speed() - self.get_load_factor()
        elif self.type == "NORWEGIAN_BLUE":
            return 0 if self.is_nailed else self.get_base_speed()

# After
class Bird:
    def get_speed(self):
        pass  # Abstract

class European(Bird):
    def get_speed(self):
        return self.get_base_speed()

class African(Bird):
    def get_speed(self):
        return self.get_base_speed() - self.get_load_factor()

class NorwegianBlue(Bird):
    def get_speed(self):
        return 0 if self.is_nailed else self.get_base_speed()
4. Java 固有のリファクタリング

最新の Java 機能を使う:

匿名クラスをラムダ式に置き換える

// Before
list.sort(new Comparator<String>() {
    @Override
    public int compare(String a, String b) {
        return a.compareTo(b);
    }
});

// After
list.sort((a, b) -> a.compareTo(b));
// Or even better
list.sort(String::compareTo);

Streams API を使う

// Before
List<String> result = new ArrayList<>();
for (String s : list) {
    if (s.length() > 3) {
        result.add(s.toUpperCase());
    }
}

// After
List<String> result = list.stream()
    .filter(s -> s.length() > 3)
    .map(String::toUpperCase)
    .collect(Collectors.toList());

文字列の連結を置き換える

// Before - inefficient in loops
String result = "";
for (String s : list) {
    result += s + ", ";
}

// After
StringBuilder result = new StringBuilder();
for (String s : list) {
    result.append(s).append(", ");
}
// Or better
String result = String.join(", ", list);

Optional を使う

// Before
public User findUser(int id) {
    User user = database.getUser(id);
    return user != null ? user : new User();
}

// After
public Optional<User> findUser(int id) {
    return Optional.ofNullable(database.getUser(id));
}

型コードを Enum に置き換える

// Before
public static final int TYPE_A = 1;
public static final int TYPE_B = 2;

// After
public enum Type {
    A, B
}
5. Python 固有のリファクタリング

リスト内包表記を使う

# Before
result = []
for item in items:
    if item > 0:
        result.append(item * 2)

# After
result = [item * 2 for item in items if item > 0]

Collections モジュールを使う

# Before
counts = {}
for item in items:
    if item in counts:
        counts[item] += 1
    else:
        counts[item] = 1

# After
from collections import Counter
counts = Counter(items)

コンテキストマネージャーを使う

# Before
file = open('file.txt')
data = file.read()
file.close()

# After
with open('file.txt') as file:
    data = file.read()

f-string を使う

# Before
message = "Hello, %s! You are %d years old." % (name, age)

# After
message = f"Hello, {name}! You are {age} years old."

型ヒントを使う

# Before
def process(data):
    return [x * 2 for x in data]

# After
def process(data: list[int]) -> list[int]:
    return [x * 2 for x in data]

データクラスを使う

# Before
class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def __repr__(self):
        return f"Point(x={self.x}, y={self.y})"

# After
from dataclasses import dataclass

@dataclass
class Point:
    x: int
    y: int
6. クリーンコードの原則

命名:

// Bad
int d; // elapsed time in days

// Good
int elapsedTimeInDays;

// Bad
public List<int[]> getThem() {
    List<int[]> list1 = new ArrayList<>();
    for (int[] x : theList) {
        if (x[0] == 4) {
            list1.add(x);
        }
    }
    return list1;
}

// Good
public List<Cell> getFlaggedCells() {
    List<Cell> flaggedCells = new ArrayList<>();
    for (Cell cell : gameBoard) {
        if (cell.isFlagged()) {
            flaggedCells.add(cell);
        }
    }
    return flaggedCells;
}

関数:

  • 小さくする(最大20~30行)
  • 1つのことだけを行う
  • 抽象化のレベルを1つにする
  • 説明的な名前を付ける
  • 引数を少なくする(0~3個が理想)

コメント:

// Bad - comment explains what code does
// Check to see if employee is eligible for full benefits
if ((employee.flags & HOURLY_FLAG) && (employee.age > 65))

// Good - code is self-explanatory
if (employee.isEligibleForFullBenefits())
7. パフォーマンスのリファクタリング

アルゴリズムの最適化:

# Before - O(n²)
def has_duplicates(arr):
    for i in range(len(arr)):
        for j in range(i + 1, len(arr)):
            if arr[i] == arr[j]:
                return True
    return False

# After - O(n)
def has_duplicates(arr):
    return len(arr) != len(set(arr))

遅延評価:

// Before - computes all values
List<Integer> results = list.stream()
    .map(this::expensiveOperation)
    .collect(Collectors.toList());
return results.get(0);

// After - computes only what's needed
return list.stream()
    .map(this::expensiveOperation)
    .findFirst()
    .orElse(null);

メモ化:

# Before
def fibonacci(n):
    if n <= 1:
        return n
    return fibonacci(n-1) + fibonacci(n-2)

# After
from functools import lru_cache

@lru_cache(maxsize=None)
def fibonacci(n):
    if n <= 1:
        return n
    return fibonacci(n-1) + fibonacci(n-2)
8. リファクタリングのワークフロー

手順:

  1. 特定: コードの不吉な臭いや改善の機会を見つけます
  2. 計画: 適用するリファクタリングを決めます
  3. テスト: テストが存在し、合格することを確認します
  4. リファクタリング: 小さな変更を1つ行います
  5. テスト: もう一度テストを実行します
  6. コミット: 動作する状態を保存します
  7. 繰り返し: 次のリファクタリングに進みます

ワークフローの例:

# 1. Create feature branch
git checkout -b refactor/improve-user-service

# 2. Identify issue (e.g., long method)
# Read code, find UserService.processUser() is 150 lines

# 3. Run existing tests
mvn test  # or pytest

# 4. Extract method
# Break processUser into smaller methods

# 5. Run tests
mvn test  # Ensure still passing

# 6. Commit
git commit -m "refactor: extract validateUser method"

# 7. Continue
# Extract next method, repeat
9. リファクタリングのチェックリスト

リファクタリングの前:

  • テストが存在し、合格する
  • コードの振る舞いを理解している
  • 明確な目標がある
  • 適用するリファクタリングが分かっている

リファクタリング中:

  • 小さく段階的な変更を行う
  • 変更のたびにテストを実行する
  • 常にコードが動作する状態を保つ
  • こまめにコミットする

リファクタリングの後:

  • すべてのテストに合格する
  • コードの可読性が向上している
  • 複雑さが軽減されている
  • 振る舞いに変更がない
  • 必要に応じてドキュメントを更新している
10. LeetCode 固有のリファクタリング

総当たりを最適化する:

// Before - Brute force O(n²)
public int[] twoSum(int[] nums, int target) {
    for (int i = 0; i < nums.length; i++) {
        for (int j = i + 1; j < nums.length; j++) {
            if (nums[i] + nums[j] == target) {
                return new int[]{i, j};
            }
        }
    }
    return new int[]{};
}

// After - Hash map O(n)
public int[] twoSum(int[] nums, int target) {
    Map<Integer, Integer> map = new HashMap<>();
    for (int i = 0; i < nums.length; i++) {
        int complement = target - nums[i];
        if (map.containsKey(complement)) {
            return new int[]{map.get(complement), i};
        }
        map.put(nums[i], i);
    }
    return new int[]{};
}

ヘルパーメソッドを抽出する:

# Before - monolithic
def solve(self, grid):
    # 50 lines of code mixing concerns

# After - modular
def solve(self, grid):
    if not self.is_valid(grid):
        return []

    processed = self.preprocess(grid)
    result = self.compute(processed)
    return self.format_output(result)

def is_valid(self, grid):
    # validation logic

def preprocess(self, grid):
    # preprocessing logic

def compute(self, data):
    # core algorithm

def format_output(self, result):
    # formatting logic

リファクタリングのアンチパターン

避けるべきこと:

  • テストなしでリファクタリングする
  • リファクタリング中に動作を変更する
  • 複数の変更を一度に行う
  • リファクタリングと機能追加を同時に行う
  • シンプルなコードを過度に設計する
  • 早まった最適化を行う

プロジェクトのコンテキスト

CS_basics リポジトリの場合:

  • leetcode_java/ と leetcode_python/ の解答をリファクタリングする
  • 言語ごとの実装間で一貫性を保つ
  • アルゴリズムの説明を最新の状態に保つ
  • プロジェクトの規約に従う
  • リファクタリングしたコードを入力例でテストする
  • 計算量の改善を記録する

ツールとリソース

IDE のリファクタリングツール:

  • IntelliJ IDEA:リファクタリングメニュー(Ctrl+Alt+Shift+T)
  • VS Code:Python のリファクタリング拡張機能
  • PyCharm:リファクタリングアクション

利用可能な場合は、自動リファクタリングを使用します:

  • 名前の変更:プロジェクト全体で安全に名前を変更します
  • メソッド/変数の抽出:自動で抽出します
  • インライン化:プレビュー付きで安全にインライン化します
  • 移動:パッケージ/モジュールを再構成します

コメント