リファクタリング
refactoring
リファクタリング
機能そのものの振る舞いは変化させず、ソースコードを読みやすく拡張しやすい形に整理する
メリット
- ソースコードを読みやすくなる
- 機能追加しやすくなる
- バグをみつけやすくする
不吉な匂い
ソースコードに以下のような特徴が見つかったら、リファクタリングをする余地がある
- if文,switch文が多い
- 同じような構文が登場する
- 双方向リンクがある
- クラス・メソッドが長い
- 意味のわからない変数がある
- 一時変数が邪魔
- 引数が多い
- if文内で正常系がわかりにくい
メソッドの抽出
長すぎるメソッドは汎用的な機能を抽出して、再利用性を高める
リファクタリング前
public void printLog(){ System.out.println("----------------------------"); System.out.println("ぶとうかのこうげき!"); System.out.println("スライムに16のダメージ!"); System.out.println("----------------------------"); System.out.println("ゆうしゃのこうげき!"); System.out.println("スライムに32のダメージ!"); System.out.println("----------------------------"); System.out.println("そうりょはバギクロスをとなえた!"); System.out.println("ドラキーAに64のダメージ!"); System.out.println("ドラキーBに64のダメージ!"); System.out.println("ドラキーCに64のダメージ!"); System.out.println("----------------------------"); }
リファクタリング後
public void printLog(){ printLine(); System.out.println("ぶとうかのこうげき!"); printDamage("スライム", 16); printLine(); System.out.println("ゆうしゃのこうげき!"); printDamage("スライム", 32); printLine(); System.out.println("そうりょはバギクロスをとなえた!"); printDamage("ドラキーA", 64); printDamage("ドラキーB", 64); printDamage("ドラキーC", 64); printLine(); } public void printLine(){ System.out.println("----------------------------"); } public void printDamage(String name, int damage){ System.out.printf("%sに%dのダメージ!\n", name, dmage); }
クラスの抽出
長すぎるクラスは役割を分担して、クラスの機能を明確にする
リファクタリング前
public class Player{ public void readBook(){} public void writeBook(){} public void buyItem(){} public void saleItem(){} }
リファクタリング後
public class Player{ public void book(){} } public class Book{ public void read(){} public void write(){} } public class Item{ public void buy(){} public void sale(){} }
引数オブジェクトの導入
複数にわたる引数は順番などが複雑になるため、頻繁に一緒に受け渡しされる値はオブジェクトにまとめる
リファクタリング前
public calc(int x1,int y1,int w1,int h1, int x2,int y2,int w2,int h2){ }
リファクタリング後
public calc(Rectangle rect1, Rectangle rect2){ }
マジックナンバーの削除
プログラミングで使われる意味のない分類のためだけの数値を、マジックナンバーと呼ぶ。
ソースコード中に含まれる数値は単体では意味がわかりにくいため、シンボリック定数にして、意味がわかりやすいようにする
リファクタリング前
area = r * r * 3.14; total = price * 1.05;
リファクタリング後
static final float PI = 3.14; static final float SHOUHI_ZEI = 1.05; area = r * r * PI; total = price * SHOUHI_ZEI;
制御構文中のフラグを削除する
for文,if文で生まれたフラグをつくると、後にフラグを変化させない場合にわかりにくい
breakやreturnでループを抜けることで、その後の記述を読まずとも理解しやすくなる
リファクタリング前
public boolean isFlag(){ booblean flag = false; for(){ if(j == 5){ flag = true; } else { } } return flag; } return flag;
リファクタリング後
public boolean isFlag(){ for(){ if(j == 5){ return true; } else { } } return false; }
タイプコードの置き換え
リファクタリング後
enum ItemType{ BOOK, DVD, SOFTWARE; } class Purchase{ private final ItemType _itemType; private final String _itemName; public Purchase (ItemType itemType, String itemName){ _itemType = itemType; _itemName = itemName; } public String buyArticle{ return _itemType+"の"+_itemName+"を買った\n"; } }
State/Strategy
public class Logger { private enum State { STOPPED { @Override public void start() { System.out.println("** START LOGGING **"); } @Override public void stop() { /* Do nothing */ } @Override public void log(String info) { System.out.println("Ignoring: " + info); } }, LOGGING { @Override public void start() { /* Do nothing */ } @Override public void stop() { System.out.println("** STOP LOGGING **"); } @Override public void log(String info) { System.out.println("Logging: " + info); } }; public abstract void start(); public abstract void stop(); public abstract void log(String info); } private State _state; public Logger() { setState(State.STOPPED); } public void setState(State state) { _state = state; } public void start() { _state.start(); setState(State.LOGGING); } public void stop() { _state.stop(); setState(State.STOPPED); } public void log(String info) { _state.log(info); } }
public class Main { public static void main(String[] args) { Logger logger = new Logger(); logger.log("information #1"); logger.start(); logger.log("information #2"); logger.start(); logger.log("information #3"); logger.stop(); logger.log("information #4"); logger.stop(); logger.log("information #5"); } }
アサーション
「この時点でこの変数はこうなっているはず」という前提を記述することで、自動チェックを行い、バグを発見しやすくする
アサーションの実行
java -ea Main
リファクタリング後
sq = w * w; assert isAbsolute(sq); private isAbsolute(int sq){ if(sq >= 0){ return true; } return false; }