Step6: リファクタリングする
コードが動くようになったら、「より良いコード」にするためのリファクタリングを検討します。ただし、1年目は過度なリファクタリングは避け、基本的な改善に留めることが重要です。
リファクタリングとは
リファクタリング = 外部から見た動作を変えずに、コードの内部構造を改善すること
目的:
- 読みやすくする
- 保守しやすくする
- バグを減らしやすくする
注意:
- 機能を追加することではない
- パフォーマンスを上げることではない(別の話)
なぜリファクタリングが必要なのか
「動くコード」と「良いコード」は違う
動くコード:
- とりあえず期待通りの結果が出る
- 自分だけが分かる
良いコード:
- 期待通りの結果が出る
- 誰が読んでも分かる
- 修正しやすい
- バグが入りにくい
1年目の現実
注意
1年目は「完璧なコード」を目指す必要はありません
1年目の優先順位:
- 動くコードを書く(最優先)
- 基本的な読みやすさを確保
- 明らかな問題を修正
やりすぎ注意:
- ❌ 完璧なリファクタリングを目指す
- ❌ 先輩が書いたコードを勝手に大きく変える
- ❌ 時間をかけすぎる
1年目で押さえるべきリファクタリング
改善1: 意味のある名前に変更する
なぜ重要か:名前が悪いと、コードを読むたびに「これは何?」と考える必要がある
// Before: 何のことか分からない
int d = 30;
String s = "John";
List<Order> list = getOrders();
// After: 意味が明確
int daysUntilExpiry = 30;
String customerName = "John";
List<Order> pendingOrders = getOrders();
ポイント:
- 略語を避ける(
cnt→count、btn→button) - 何を表しているか分かる名前にする
- 長くなっても意味が伝わる方が良い
改善2: マジックナンバーを定数に
なぜ重要か:数字だけでは意味が分からず、修正時に漏れが生じる
// Before: 30って何?0.1って何?
if (days > 30) {
discount = price * 0.1;
}
// After: 意味が明確
private static final int PREMIUM_THRESHOLD_DAYS = 30;
private static final double PREMIUM_DISCOUNT_RATE = 0.1;
if (days > PREMIUM_THRESHOLD_DAYS) {
discount = price * PREMIUM_DISCOUNT_RATE;
}
ポイント:
- 意味のある名前をつける
- 同じ値を複数箇所で使う場合は特に重要
- 仕様変更時に1箇所の修正で済む
改善3: 長いメソッドを分割する
なぜ重要か:長いメソッドは理解が難しく、テストも困難
// Before: 100行のメソッド
public void processOrder(Order order) {
// バリデーション(20行)
...
// 在庫チェック(15行)
...
// 計算処理(30行)
...
// DB登録(20行)
...
// 通知送信(15行)
...
}
// After: 役割ごとに分割
public void processOrder(Order order) {
validateOrder(order);
checkStock(order);
calculateTotal(order);
saveToDatabase(order);
sendNotification(order);
}
private void validateOrder(Order order) {
// バリデーション処理
}
private void checkStock(Order order) {
// 在庫チェック処理
}
// ...
ポイント:
- 1つのメソッドは1つの責任
- 目安は20〜30行以内
- メソッド名で何をするか分かるように
改善4: ネストを減らす
なぜ重要か:深いネストは読みにくく、バグを見逃しやすい
// Before: 深いネスト
public void process(Order order) {
if (order != null) {
if (order.getItems() != null) {
if (!order.getItems().isEmpty()) {
for (Item item : order.getItems()) {
if (item.getStock() > 0) {
// 処理
}
}
}
}
}
}
// After: 早期リターンでフラットに
public void process(Order order) {
if (order == null) return;
if (order.getItems() == null) return;
if (order.getItems().isEmpty()) return;
for (Item item : order.getItems()) {
if (item.getStock() <= 0) continue;
// 処理
}
}
ポイント:
- 異常系は早期リターン
- ループ内の条件は
continueで抜ける - ネストは2〜3階層が目安
改善5: 重複コードをまとめる
なぜ重要か:同じコードが複数あると、修正漏れが発生する
// Before: 同じ処理が重複
public void createUser(User user) {
if (user.getName() == null || user.getName().isEmpty()) {
throw new ValidationException("名前は必須です");
}
// ...
}
public void updateUser(User user) {
if (user.getName() == null || user.getName().isEmpty()) {
throw new ValidationException("名前は必須です");
}
// ...
}
// After: 共通メソッドに抽出
public void createUser(User user) {
validateUserName(user);
// ...
}
public void updateUser(User user) {
validateUserName(user);
// ...
}
private void validateUserName(User user) {
if (user.getName() == null || user.getName().isEmpty()) {
throw new ValidationException("名前は必須です");
}
}
ポイント:
- 2回以上同じコードがあれば検討
- ただし、たまたま同じだけで意味が違う場合は分けたまま
リファクタリングの進め方
1. 動作確認できる状態を維持
重要:リファクタリング前と後で、動作が変わっていないことを確認する
方法:
- 自動テストがあれば実行
- なければ手動で動作確認
2. 小さく、少しずつ
- ❌ 悪い進め方:大規模な変更を一気に行う → どこで壊れたか分からない
- ✅ 良い進め方:小さな改善を1つずつ行う → 問題があればすぐに戻せる
3. コミットを細かく
# リファクタリングごとにコミット
git commit -m "変数名をより明確な名前に変更"
git commit -m "calculateTotalメソッドを抽出"
git commit -m "早期リターンでネストを削減"
1年目がリファクタリングで注意すべきこと
注意1: 他人のコードを大きく変えない
- ❌ やってはいけない:先輩が書いた100行のコードを「読みにくいから」と勝手に書き換える
- ✅ やっていいこと:
- 自分が書いたコードを改善
- レビューで指摘されたコードを修正
- 先輩に相談してから変更
注意2: 時間をかけすぎない
- ❌ 完璧を目指して何時間もかける → 本来のタスクが終わらない
- ✅ 基本的な改善にとどめる → 動くコード > 完璧なコード
注意3: 機能追加と混ぜない
- ❌ リファクタリングと機能追加を同時にやる → バグの原因が分からなくなる → レビューが大変
- ✅ 分けてコミット:
- リファクタリングだけをコミット
- 機能追加を別コミット
リファクタリングのタイミング
やるべきタイミング
- コードレビュー前:指摘を減らせる
- バグ修正のついで:原因となった分かりにくいコードを改善
- 新機能追加の前:追加しやすい構造に整える
避けるべきタイミング
- 締め切り直前:リスクが高い
- よく分からないコード:意図を理解してから
- テストがないコード:壊れても分からない
実践チェックリスト
リファクタリング時は、以下を確認:
基本
改善内容
注意点
次のステップ
コードを整えたら、提出前の最終確認としてセルフチェックを行います。