Skip to content

Lesson 9: 簡単なリファクタリング

クラス: 初級
所要時間: 約40分
前提知識: 初級 Lesson 1〜8 完了済み


1. 解説

リファクタリングとは

リファクタリング = 外から見た動作を変えずに、内部の構造を改善すること

Before: 動くが汚い
After:  同じ動作でも読みやすく・保守しやすい

よくあるリファクタリングパターン

パターンBeforeAfter
関数の抽出長い関数小さな関数に分割
変数の名前変更d, tmp, xdaysElapsed, tempBuffer, userId
条件の簡略化入れ子のifEarly return / Guard clause
マジックナンバー排除* 0.1* TAX_RATE
Promise → async/awaitコールバック地獄読みやすい非同期
重複コードの統合コピペコード共通関数に抽出

Claude Code へのリファクタリング指示

bash
# 特定パターンの適用
「この関数を Guard clause パターンで書き直してください」

# 全体的な改善
「このファイルをリファクタリングしてください。
 外部から見た動作は変えないこと。
 特に読みやすさと保守性を優先してください」

# 段階的なリファクタリング
「まず関数の抽出だけ行ってください。
 名前変更は次のステップで行います」

2. 実践

Step 1: リファクタリング対象を用意

bash
mkdir -p ~/claude-practice/elementary/lesson9
cd ~/claude-practice/elementary/lesson9

cat > messy_order.js << 'EOF'
function processOrder(o) {
  var t = 0;
  var x = [];
  for(var i = 0; i < o.items.length; i++) {
    var item = o.items[i];
    if(item.qty > 0) {
      if(item.price > 0) {
        t = t + (item.price * item.qty);
        x.push(item);
      }
    }
  }
  var disc = 0;
  if(o.code != null && o.code != undefined && o.code != '') {
    if(o.code == 'SAVE10') {
      disc = t * 0.1;
    } else if(o.code == 'SAVE20') {
      disc = t * 0.2;
    } else if(o.code == 'HALFOFF') {
      disc = t * 0.5;
    } else {
      disc = 0;
    }
  }
  var sub = t - disc;
  var tx = sub * 0.1;
  var total = sub + tx;
  if(total > 10000) {
    var ship = 0;
  } else if(total > 5000) {
    var ship = 500;
  } else {
    var ship = 800;
  }
  return {
    items: x,
    subtotal: t,
    discount: disc,
    tax: tx,
    shipping: ship,
    total: total + ship
  };
}

module.exports = { processOrder };
EOF

Step 2: 段階的リファクタリング

bash
claude

第1段階: 変数名の改善

messy_order.js を読んでください。
まず変数名・関数名だけを改善してください。
ロジックは変更しないこと。

第2段階: マジックナンバーの排除

次に、コード内のマジックナンバー(0.1, 0.2, 0.5, 10000, 5000, 500, 800)を
定数として定義してください。

第3段階: 関数の抽出

processOrder 関数が長すぎます。
以下の関数を抽出してください:
- calculateValidItems(items): 有効なアイテムの合計と一覧
- applyDiscount(subtotal, code): 割引計算
- calculateShipping(total): 送料計算

第4段階: Guard clause の適用

各関数にGuard clause パターンを適用して、
ネストを浅くしてください。

Step 3: 動作が変わっていないことを検証

bash
# リファクタリング前後で同じ結果になるか確認
node -e "
const { processOrder } = require('./messy_order');
const order = {
  items: [{price:1000,qty:2},{price:500,qty:3},{price:-100,qty:1}],
  code: 'SAVE10'
};
console.log(JSON.stringify(processOrder(order), null, 2));
"

3. 確認課題(Assignment)

課題 9-1: コールバック地獄の解消

bash
cat > ~/claude-practice/elementary/lesson9/callback_hell.js << 'EOF'
function getUserData(userId, callback) {
  setTimeout(() => {
    getUser(userId, (err, user) => {
      if (err) { callback(err); return; }
      getOrders(user.id, (err, orders) => {
        if (err) { callback(err); return; }
        getProducts(orders[0].productId, (err, product) => {
          if (err) { callback(err); return; }
          getReviews(product.id, (err, reviews) => {
            if (err) { callback(err); return; }
            callback(null, { user, orders, product, reviews });
          });
        });
      });
    });
  }, 100);
}

// 各関数はコールバック形式(省略)
function getUser(id, cb) { cb(null, { id, name: 'Alice' }); }
function getOrders(userId, cb) { cb(null, [{ productId: 1, total: 1000 }]); }
function getProducts(id, cb) { cb(null, { id, name: 'Book' }); }
function getReviews(productId, cb) { cb(null, [{ rating: 5 }]); }

module.exports = { getUserData };
EOF
callback_hell.js を async/await を使ってリファクタリングしてください。
各コールバック関数を Promise を返す関数に変換して、
getUserData を async 関数に書き直してください。

達成条件: 同じ動作でコールバックがゼロになること

課題 9-2: 重複コードの統合

bash
cat > ~/claude-practice/elementary/lesson9/duplicated.js << 'EOF'
function renderUserCard(user) {
  const div = document.createElement('div');
  div.className = 'card user-card';
  const title = document.createElement('h3');
  title.textContent = user.name;
  const subtitle = document.createElement('p');
  subtitle.textContent = user.email;
  const badge = document.createElement('span');
  badge.className = `badge badge-${user.role}`;
  badge.textContent = user.role;
  div.appendChild(title);
  div.appendChild(subtitle);
  div.appendChild(badge);
  return div;
}

function renderProductCard(product) {
  const div = document.createElement('div');
  div.className = 'card product-card';
  const title = document.createElement('h3');
  title.textContent = product.name;
  const subtitle = document.createElement('p');
  subtitle.textContent = `¥${product.price}`;
  const badge = document.createElement('span');
  badge.className = `badge badge-${product.category}`;
  badge.textContent = product.category;
  div.appendChild(title);
  div.appendChild(subtitle);
  div.appendChild(badge);
  return div;
}
EOF
duplicated.js の重複コードを抽出して共通関数を作ってください。
renderUserCard と renderProductCard はその共通関数を使うようにしてください。

達成条件: 重複コードが共通関数にまとめられ、元の関数が薄いラッパーになること

課題 9-3: 複合リファクタリング

初級 Lesson 3 で修正した buggy_app.js を元に(または新たに作成して):

以下のリファクタリングをすべて適用してください:
1. マジックナンバーを定数に
2. 関数を単一責務に
3. async/await の活用(現在同期なら非同期版も用意)
4. Guard clause で早期リターン
5. エラーハンドリングの統一

変更前後の行数の変化と可読性の変化を報告してください。

達成条件: コードが読みやすくなりながら、同じ動作を維持していること


自己チェックリスト


次の型

Lesson 10: エラーログの解析 →

Claude Code Dojo