Lesson 9: 簡単なリファクタリング
クラス: 初級
所要時間: 約40分
前提知識: 初級 Lesson 1〜8 完了済み
1. 解説
リファクタリングとは
リファクタリング = 外から見た動作を変えずに、内部の構造を改善すること
Before: 動くが汚い
After: 同じ動作でも読みやすく・保守しやすいよくあるリファクタリングパターン
| パターン | Before | After |
|---|---|---|
| 関数の抽出 | 長い関数 | 小さな関数に分割 |
| 変数の名前変更 | d, tmp, x | daysElapsed, tempBuffer, userId |
| 条件の簡略化 | 入れ子のif | Early 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 };
EOFStep 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 };
EOFcallback_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;
}
EOFduplicated.js の重複コードを抽出して共通関数を作ってください。
renderUserCard と renderProductCard はその共通関数を使うようにしてください。達成条件: 重複コードが共通関数にまとめられ、元の関数が薄いラッパーになること
課題 9-3: 複合リファクタリング
初級 Lesson 3 で修正した buggy_app.js を元に(または新たに作成して):
以下のリファクタリングをすべて適用してください:
1. マジックナンバーを定数に
2. 関数を単一責務に
3. async/await の活用(現在同期なら非同期版も用意)
4. Guard clause で早期リターン
5. エラーハンドリングの統一
変更前後の行数の変化と可読性の変化を報告してください。達成条件: コードが読みやすくなりながら、同じ動作を維持していること
