第2章_Java言語で学ぶリファクタリング入門

第2章で学ぶこと

"制御フラグの削除"
というリファクタリングを学びます。


目新しいことは無かったので、
メモがまんま写してします。(写経はしました。)

名前 <<制御フラグの削除>>
英語名 Remove Control Flag
文脈 処理の流れを制御している
問題 処理の流れが制御フラグでコントロールされており、コードが複雑になっている。
解法 制御フラグを消し、その代わりにbreak,continue,returnを使いましょう
結果 ○条件の意味と、制御の流れが明確になる。×単純なループにも無理に適用すると、返ってコードが不自然になる。
手順 (break,continueを使う場合)
(1)制御フラグでコントロールされているループを見つける
(2)制御フラグへの代入をbreakやcontinueで置き換える
(3)コンパイルしてテストする

(returnを使う場合)
(1)制御フラグでコントロールされているループを見つける
(2)そのループを新たなメソッドとして抽出する
(3)制御フラグへの代入をreturnで置き換える
(4)コンパイルしてテストする
関連 <<メソッドの抽出>>
制御フラグでコントロールされているループを新たなメソッドとして抽出する時に用いる

「変数名の変更」リファクタリングの前に、意味がわかるように制御フラグの名前を書き換えるとよい

実演その1

プログラム概要
ベタ書きのint配列中に、特定の要素があるかどうかを調べるプログラム。


<<制御フラグの削除>>

  • <<制御フラグの削除>>を実行する前に、変数名を分かりやすく変更します。
  • foundにtrueを代入した時点でfor文をbreakすることにする。
  • breakの部分をreturnを使ってさらに変更します。戻り値にはtrueを直接指定します。

before

FindIntクラス
package two.findint.before;

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;
	}
}
Mainクラス
package two.findint.before;

public class Main {

	/**
	 * @param args
	 */
	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...");
		}

	}

}

after

FindIntクラス
package two.findint.after;
/*
 * <<削除フラグの削除>>
 * 
 * <ul>
 * <li><<削除フラグの削除>>を実行する前に、変数名を分かりやすく変更します。</li>
 * <li>foundにtrueを代入した時点でfor文をbreakすることにする。</li>
 * <li>breakの部分をreturnを使ってさらに変更します。戻り値にはtrueを直接指定します。</li>
 * </ul>
 * */

public class FindInt {
	public static boolean find(int[] data, int target){
		for (int i = 0; i < data.length; i++){
			if(data[i] == target){
				return true;
			}
		}
		return false;
	}
}
Mainクラス
package two.findint.before;

public class Main {

	/**
	 * @param args
	 */
	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...");
		}

	}

}

実演その2

プログラム概要
外部ファイル(dbfile.txt)に書いてあることを読み込み、
「メールアドレス」と「名前」に分けて表示するプログラム。

before

dbfile.txt

hyuki@example.com=Hiroki Yuki
sato@example.com=Sato Hanako
tomura@example.com=Tomura

Mainクラス
package two.simpledatabase.before;

import java.io.FileReader;
import java.io.IOException;
import java.util.Iterator;

public class Main {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		try{
		    SimpleDatabase db = new SimpleDatabase(new FileReader("dbfile.txt"));
		    Iterator<String> it = db.iterator();
		    while(it.hasNext()){
		    	String key = it.next();
		    	System.out.println("KEY: " + key + "");
		    	System.out.println("VALUE:" + db.getValue(key) + "");
		    	System.out.println();
		    }
		}catch(IOException e){
			e.printStackTrace();
		}
	}

}
SimpleDatabaseクラス
package two.simpledatabase.before;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.Reader;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

public class SimpleDatabase {
	private Map<String, String> _map = new HashMap<String,String>();
	
	public SimpleDatabase (Reader r1) throws IOException{
		BufferedReader r2 = new BufferedReader(r1);
		boolean flag = false;
		String tmp;
		while(!flag){
			tmp = r2.readLine();
			if(tmp == null){
				flag = true;
			}else{
				boolean flag2 = true;
				StringBuffer s1 = new StringBuffer();
				StringBuffer s2 = new StringBuffer();
				
				for(int i = 0; i < tmp.length(); i++){
					char tmp2 = tmp.charAt(i);
					if(flag2){
						if(tmp2 == '='){
							flag2 = false;
						}else{
							s1.append(tmp2);
						}
					}else{
						s2.append(tmp2);
					}
				}
				
				String ss1 = s1.toString();
				String ss2 = s2.toString();
				_map.put(ss1, ss2);				
			}
		}
	}
	
	public void putValue(String key, String value){
		_map.put(key, value);
	}
	
	public String getValue(String key){
		return _map.get(key);
	}
	
	public Iterator<String> iterator(){
		return  _map.keySet().iterator();
	}
}

after

SimpleDatabaseクラスのみを変更し、Mainクラスとdbfile.txtには変更を加えていません。
そのため、SimpleDatabaseクラスのみを載せます。

SimpleDatabaseクラス
package two.simpledatabase.after;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.Reader;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;


/**
 *<<制御フラグの削除>>
 *
 *<ul>
 *<li><<制御フラグの削除>>を実行する前に、変数名を分かりやすく変更しておきましょう。</li>
 *</ul>
 *
 *<<制御フラグの削除>>を実行する前に、変数名を分かりやすく変更しておきましょう。
 *==============================================================
 *r1    → r
 *r2    → reader
 *flag  → reading
 *flag2 → scanningKey
 *tmp   → line
 *tmp2  → c
 *s1    → keyBuffer
 *s2    → valueBuffer
 *ss1   → key
 *ss2   → value
 *==============================================================
 *
 *
 *該当のメソッドがクラスライブラリになるのではないかなと考える。
 *indexOf,substringを知る。
 * */
public class SimpleDatabase {
	private Map<String, String> _map = new HashMap<String,String>();
	
	public SimpleDatabase (Reader r) throws IOException{
		BufferedReader reader = new BufferedReader(r);
		String line;
		while(true){
			line = reader.readLine();
			if(line == null){
				break;
			}else{
				for(int i = 0; i < line.length(); i++){
					
					//indexOf・・・
                    //文字列の中に特定の文字が出てくる場所を見つける。
					int equalIndex = line.indexOf("=");
					if(equalIndex > 0){
						String key   = line.substring(0,equalIndex);
						String value = line.substring(equalIndex + 1, line.length());
						_map.put(key, value);				
					}
				}
			}
		}
	}
	
	public void putValue(String key, String value){
		_map.put(key, value);
	}
	
	public String getValue(String key){
		return _map.get(key);
	}
	
	public Iterator<String> iterator(){
		return  _map.keySet().iterator();
	}
}