「5分」駆け出しのPGなら、必見!Javaリファクタリング方法3選
初めてシステムのメンテナンスした時、システムのソースコードはわかりにくくなっていました。
めまいがしました。私は「これ、どうしよう…」と思いました。その時、非常に役立ったリファクタリングの本『Java言語で学ぶリファクタリング入門』を読んで、一番簡単にコードを改善する方法を紹介したいです。
リファクタリングというのは?
外部から見たプログラムの振る舞いを変えずに、
プログラム内部の構造を改善することです。
なぜ、リファクタリングしますか?
● バグを見つけやすくするため
● 機能追加しやすくするため
● レビューしやすくするため
つまり、可読性を高める効果があります。
いつ、リファクタリングしますか?
いろいろ場合がありまぐが、体表的なケース二つをおぼえてください!
● 重複したコード
● 長すぎるメソッド
リファクタリングの方法は?
この記事では絶対に役立つ三つを覚えておきましょう!
1.マジックナンバーを超えて、シンボリック定数を志向しよう
マジックナンバーというのは?
具体的な数値がソースコードのなかでうまこめれていること。
意味が不明瞭ため、メンテナンスがしにくくなる。
シンボリック定数というのは?
プログラム言語のソースコード中に記述される名前化された定数。
NGなコード
// 200という数が何の意味かまったくわからない。
if (200 < input.length()) {
...
}
リファクタリング後
// 200という数の意味はすぐ分かる!
public static final int MAX_INPUT_LENGTH = 200;
if (MAX_INPUT_LENGTH < input.length()) {
...
}
リファクタリング前のコードの200は意味を全然わからないですが、
リファクタリング後はシンボリック定数を使って200の意味が入力最大値ということをわかります。
もっと、いつの例を考えましょう。
NGなコード
public class Programmer {
private final String _name;
public Programmer(String name) {
_name = name;
}
public class void order(int command) {
if (command == 0) {
System.out.println(_name + "is working");
} else if (command == 1) {
System.out.println(_name + "is arguing");
} else if (command == 2) {
System.out.println(_name + "is taking a rest");
} else {
System.out.println("Command error. command = " + command);
}
}
}
public class Main {
public static void main (String[] args) {
Programmer programmer = new Programmer("Tanaka");
programmer.order(0); // work
programmer.order(1); // argue
programmer.order(2); // rest
}
}
Mainクラスのコメントを読まないと引数(0,1,2)の意味はわかりにくいです。
リファクタリング後
public class Programmer {
//シンボリック定数の変数を宣言
public static final int COMMAND_WORK = 0;
public static final int COMMAND_ARGUE = 1;
public static final int COMMAND_REST = 2;
private final String _name;
public Programmer(String name) {
_name = name;
}
public class void order(int command) {
// シンボリックを引数に適用
if (command == COMMAND_WORK) {
System.out.println(_name + "is working");
} else if (command == COMMAND_ARGUE) {
System.out.println(_name + "is arguing");
} else if (command == COMMAND_REST) {
System.out.println(_name + "is taking a rest");
} else {
System.out.println("Command error. command = " + command);
}
}
}
public class Main {
public static void main (String[] args) {
Programmer programmer = new Programmer("Tanaka");
// シンボリックで関数を呼び出す
programmer.order(Programmer.COMMAND_WORK);
programmer.order(Programmer.COMMAND_ARGUE);
programmer.order(Programmer.COMMAND_REST);
}
}
シンボリック定数の変数を宣言して、リファクタリングしました。
そしたら、引数だけでも、意味が明らかになります。
2.breakとreturnを用いて制御フラグの削除しよう
フラグというのは?
状態を記録し、勝利の流れを制御するためのBoolean型の変数です。
制御フラグは処理の流れを制御するときに用いるフラグという意味です。
フラグは悪い?
必ずしも悪いものではありませんが、
用いすぎると処理の流れがわかりにくくなります。
breakとreturnを利用してコードを読みわすくします。
NGなコード
flag = true;
while (flag) {
...
if (A) {
flag = false;
} else {
...
}
}
リファクタリング後
while (true) {
...
if (A) {
break;
} else {
...
}
}
いつの例を考えましょう。
NGなコード
public class FindInt {
public static boolean find(int[] data, int target) {
boolean flag = false;
for (int i = 0; i < data.length && !flag; i++) {
if (data[i] == target) {
flag = true;
}
}
return flag;
}
}
public class Main {
public static void main(String[] args) {
int[] data = {
1, 9, 0, 2, 8, 5, 6, 3, 4, 7,
};
if (FindInt.find(data, 5)) {
System.out.println("Found");
} else {
System.out.println("Not Found");
}
}
}
flagがいろいろなところにありますが、特に「&& !flag」部分が処理の流れがわかりにくくなりますね。
リファクタリング後
public class FindInt {
public static boolean find(int[] data, int taret) {
//1.条件の修正
for (int i = 0; i < data.length; i++) {
if (data[i] == target) {
//2.returnを使う
return true;
}
}
//3.returnを使う
return false;
}
}
1.for文の条件が簡単になりますた。
2/3. returnを用いて、処理の流れがわかりやすくなりました。
breakとreturnの可読性が高い理由?
目にした時点、コードの先を読む必要がなくなるためです。
flag = true;
while (flag) {
if (条件A) {
flag = false; // 以降、flagがtrueになる可能性もある
}
...
...
...
}
while (true) {
if (条件A) {
break; // 以降、while文は終了
}
3.メソッドを抽出しよう
一つのメソッドが長すぎる場合、メソッドのからグループ化できるコードを抜き出します。
public void print(int times) {
// Prints border
System.out.print("+");
for (int i = 0; i < _content.length(); i++) {
System.out.println("-");
}
System.out.println("+");
// Prints content.
for (int i = =0; i < times; i++) {
System.out.print("-");
}
// Prints border
System.out.print("+");
for (int i = 0; i < _content.length(); i++) {
System.out.println("-");
}
System.out.println("+");
}
抽出順番
1.新しいメソッドの名前をつける。(printBorder、printContent)
2.元のメソッドから、新しいメソッドへコードをコピーする。
3.新しいメソッドのローカル変数やパラメータを検討する。
4.新しいメソッドを呼び出す。
public void print(int times) {
printBorder();
printContent(times);
printBorder();
}
private void printBorder() {
System.out.print("+");
for (int i = 0; i < _content.length(); i++) {
System.out.println("-");
}
System.out.println("+");
}
private void printContent(int times) {
for (int i = 0; i < times; i++) {
System.out.print("-");
}
}
まとめ
システムのメンテナンスのためにClean Codeは非常に重要です。
最も基本的ですが、また最も重要な 3 つのリフェクトリング方式でシステムメンテナンスに貢献してみましょう。
エンジニアファーストの会社 株式会社CRE-CO
ソンさん
この記事が気に入ったらサポートをしてみませんか?