リファクタリングって何? コードをきれいに保つ秘訣

docs

ここまでたくさんのコードを紹介したよね。動くコードは素晴らしいんだけど、それだけで満足しちゃダメなんだ。今回は、もっと良いコードを書くための「リファクタリング」について解説していくよ。リファクタリングは、コードの見た目をきれいにしたり、わかりやすくしたりする作業のこと。機能を変えずにコードを改善するイメージだね。

なんでリファクタリングが大切なの?

「動くならこのままでいいじゃん!」って思うかもしれないけど、リファクタリングにはたくさんのメリットがあるんだ。

1. バグが減る!

ごちゃごちゃしたコードは、バグの温床になりやすいんだ。例えば、もしあなたが書いたコードがこんな感じだったらどうかな?

Java

public class Calculator {
    public int calculate(int a, int b, String operation) {
        if (operation.equals("add")) {
            return a + b;
        } else if (operation.equals("subtract")) {
            return a - b;
        } else if (operation.equals("multiply")) {
            return a * b;
        } else if (operation.equals("divide")) {
            if (b != 0) {
                return a / b;
            } else {
                System.out.println("Error: Cannot divide by zero.");
                return 0;
            }
        } else {
            System.out.println("Error: Invalid operation.");
            return 0;
        }
    }
}

このcalculateメソッド、ちょっとごちゃっとしてるよね? もし新しい演算を追加しようとしたら、さらに条件分岐が増えて、どこかにバグが紛れ込んじゃう可能性が高くなるんだ。でも、リファクタリングしてコードを整理すると、それぞれの処理が明確になってバグを見つけやすくなるし、新しい機能を追加するときもミスが減るんだ。

2. コードが読みやすくなる!

数週間後、数ヶ月後に自分の書いたコードを見返してみて、「これ、何のために書いたんだっけ?」ってなった経験はないかな? コードが読みやすいと、そんな悩みも減るんだ。他の人があなたのコードを読むときも、スムーズに理解できるから、チームでの開発もしやすくなるよ。

例えば、こんなコードがあったとしよう。

Java

public class UserProcessor {
    public void p(String n, int a, String e) {
        // ユーザー情報を処理する
        // ...
    }
}

メソッド名がp、引数がn, a, eじゃ、何をしているのか全然わからないよね。これをリファクタリングして、もっと分かりやすくするんだ。

Java

public class UserProcessor {
    public void processUser(String userName, int userAge, String userEmail) {
        // ユーザー情報を処理する
        // ...
    }
}

これなら、一目で何をするメソッドで、どんな情報を受け取るのかがわかるよね!

3. 開発スピードが上がる!

一見すると、「リファクタリングに時間を使うなら、新しい機能を作った方がいいじゃん!」って思うかもしれない。でも、コードがきれいだと、後から機能を追加したり修正したりする手間がグッと減るんだ。結果的に、開発全体のスピードが上がるんだよ。

例えるなら、ごちゃごちゃした部屋で探し物をするのと、きちんと整理された部屋で探し物をするのと、どっちが早く見つかるかな? コードも同じで、整理されている方が作業がスムーズに進むんだ。

リファクタリングの具体的なテクニック(Java編)

じゃあ、具体的にどんな風にリファクタリングしていくのか、いくつかのテクニックを紹介するね。

1. 変数名やメソッド名を分かりやすくする

さっきの例でも出したけど、これが一番基本中の基本。何をしているか一目でわかるような名前に変えよう。

悪い例:

Java

int x = 10; // 数値 String s = "hello"; // 文字列

良い例:

Java

int quantity = 10; // 数量 String greetingMessage = "hello"; // 挨拶メッセージ

メソッド名も同じだよ。

悪い例:

Java

public void calc() { ... } // 計算

良い例:

Java

public double calculateTotalPrice() { ... } // 合計金額を計算する

    2. 長いメソッドを分割する

    一つのメソッドにたくさんの処理が詰め込まれていると、何をしているのか分かりにくくなるんだ。そういう時は、役割ごとに小さなメソッドに分割しよう。

    元のコード:

    Java

    public class OrderProcessor {
        public void processOrder(Order order) {
            // 1. 在庫チェック
            if (!checkStock(order)) {
                System.out.println("在庫が足りません。");
                return;
            }
    
            // 2. 注文金額の計算
            double totalAmount = calculateAmount(order);
    
            // 3. 支払い処理
            if (!processPayment(order, totalAmount)) {
                System.out.println("支払い処理に失敗しました。");
                return;
            }
    
            // 4. 注文履歴の保存
            saveOrderHistory(order);
    
            System.out.println("注文が正常に処理されました。");
        }
    
        private boolean checkStock(Order order) {
            // 在庫チェックのロジック
            return true; // 仮
        }
    
        private double calculateAmount(Order order) {
            // 金額計算のロジック
            return 100.0; // 仮
        }
    
        private boolean processPayment(Order order, double amount) {
            // 支払い処理のロジック
            return true; // 仮
        }
    
        private void saveOrderHistory(Order order) {
            // 注文履歴保存のロジック
        }
    }
    

    このprocessOrderメソッドは、在庫チェック、金額計算、支払い処理、注文履歴の保存と、たくさんのことをやっているよね。これを、それぞれ別のメソッドに分けてみよう。

    リファクタリング後:

    Java

    public class OrderProcessor {
        public void processOrder(Order order) {
            if (!isStockAvailable(order)) {
                System.out.println("在庫が足りません。");
                return;
            }
    
            double totalAmount = calculateOrderAmount(order);
    
            if (!makePayment(order, totalAmount)) {
                System.out.println("支払い処理に失敗しました。");
                return;
            }
    
            saveOrder(order);
    
            System.out.println("注文が正常に処理されました。");
        }
    
        private boolean isStockAvailable(Order order) {
            // 在庫チェックのロジック
            return true; // 仮
        }
    
        private double calculateOrderAmount(Order order) {
            // 金額計算のロジック
            return 100.0; // 仮
        }
    
        private boolean makePayment(Order order, double amount) {
            // 支払い処理のロジック
            return true; // 仮
        }
    
        private void saveOrder(Order order) {
            // 注文履歴保存のロジック
        }
    }
    

    こうすることで、processOrderメソッドの役割は「注文を処理する」という上位の概念になり、それぞれの詳細な処理は別のメソッドに任せることができたね。これなら、もし在庫チェックのロジックだけ変更したくなったとしても、isStockAvailableメソッドだけを見ればいいから、影響範囲も小さくなるんだ。

    3. 重複するコードをまとめる

    同じようなコードが何箇所にも書かれている場合、それを一つのメソッドにまとめてしまおう。こうすることで、変更があったときに一箇所だけ修正すればよくなるし、コード量も減らすことができるんだ。

    悪い例:

    Java

    public void printWelcomeMessageForAdmin() { 
      System.out.println("--- Welcome to Admin Panel ---"); 
      System.out.println("You have admin privileges."); 
    } 
    
    public void printWelcomeMessageForUser() { 
      System.out.println("--- Welcome to User Panel ---"); 
      System.out.println("You are a regular user."); 
    }

    「— Welcome to … —」の部分が重複しているよね。

    良い例:

    Java

    public void printWelcomeMessage(String panelName, String userTypeMessage) { 
      System.out.println("--- Welcome to " + panelName + " ---");   
      System.out.println(userTypeMessage); 
    } 
    
    public void printWelcomeMessageForAdmin() { 
      printWelcomeMessage("Admin Panel", "You have admin privileges."); 
    } 
    
    public void printWelcomeMessageForUser() { 
      printWelcomeMessage("User Panel", "You are a regular user."); 
    }

    共通する部分をprintWelcomeMessageメソッドにまとめたことで、スッキリしたよね。

    4. コメントを整理する

    コメントはコードを理解するのに役立つけど、多すぎたり、情報が古かったりすると、かえって邪魔になることもあるんだ。リファクタリングしてコードが分かりやすくなれば、コメントの量を減らせることもあるよ。そして、コメントを書くなら、「何を」しているかではなく、「なぜ」そうしているのかを書くように心がけよう。

    悪い例:

    Java

    // ユーザー名を設定する user.setUserName(name);

    こんなコメントは、コードを見ればわかるからいらないよね。

    良い例:

    Java

    // ユーザー名が20文字を超える場合は短縮する(表示上の制約のため) if (name.length() > 20) {   name = name.substring(0, 20);} user.setUserName(name);

    これは「なぜ」短縮するのかが書かれているから、意味のあるコメントだね。

    いつリファクタリングするの?

    リファクタリングは、いつでも好きな時にやればいいわけじゃないんだ。おすすめのタイミングはいくつかあるよ。

    • 機能を追加する前: 新しい機能を追加する前に、既存の関連コードをきれいにすると、新しい機能をスムーズに組み込めるよ。
    • バグを修正した後: バグが見つかった箇所は、コードが複雑になっていることが多い。修正するついでに、その周辺もきれいにすると良いね。
    • コードレビューの時: チームでコードレビューをする際に、リファクタリングの提案をしたり、提案されたりすることもあるよ。
    • 「ちょっと変だな」と感じた時: 自分でコードを書いていて、「なんか読みにくいな」「もっと良い書き方があるはず」と感じたら、それがリファクタリングのチャンス!

    ただし、動かないコードをリファクタリングするのはNGだよ。まずは動くコードを書いて、テストで問題ないことを確認してからリファクタリングしようね。

    まとめ

    リファクタリングは、コードをきれいに保つための大切な習慣だよ。最初から完璧なコードを書くのは難しいから、後から見直して改善していく意識が重要なんだ。

    • バグを減らす
    • コードを読みやすくする
    • 開発スピードを上げる

    これらのメリットを理解して、少しずつリファクタリングを実践してみてほしいな。初めは難しく感じるかもしれないけど、慣れてくると「きれいなコードを書く楽しさ」がわかってくるはずだよ。


    このブログの他の記事も読んでみてね!

    コメント

    タイトルとURLをコピーしました