「Javaプログラミング能力認定試験課題プログラムのリファクタリングレポート」に触発されて

ryoasai さんの「Javaプログラミング能力認定試験課題プログラムのリファクタリングレポート」に触発されたので、
Java の勉強がてら、同様なプログラムを作成してみる。

Step 5

このプログラムには、以下のような処理が繰り返し現れる。

        try {
            BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));

            while(true) {
                System.out.println("何かを入力しろ");
                System.out.print(" [x, x, x, E]>");

                String s = reader.readLine();
                if (s == null) break;
                if (s.equals("E")) break;

                if (s.equals("x"))
                    funcX();
                else if (s.equals("x"))
                    funcX();
            }
            reader.close();

        } catch (Exception e) {
            System.out.println(e);
        }

この部分を以下のような抽象クラスとして定義する。
(ryoasai さんのソースを元にしています。)

abstract class AbstractDispatcher {
    protected BufferedReader reader;

    AbstractDispatcher(BufferedReader reader) {
        this.reader = reader;
    }

    protected void run() {
        while (true) {
            // 選択肢 を 表示
            beforeDisplayMenu();
            
            // 処理 を 選択
            String inputCode = printMenuAndWaitForInput();

            // 終了?
            if (isEndCommand(inputCode)) break;
            
            // 選択した処理 を 実行
            if (runFunction(inputCode)) break;
        }
    }

    // 選択肢 を 表示
    protected abstract void beforeDisplayMenu();

    // 処理 を 選択
    protected String printMenuAndWaitForInput() {
        String inputCode;
        try {
            inputCode = reader.readLine();

        } catch (Exception e) {
            e.printStackTrace();
            inputCode = null;
        }

        return inputCode;
    }

    // 終了?
    protected boolean isEndCommand(String inputCode) {
        if (inputCode == null) return true;
        return "E".equals(inputCode);
    }

    // 選択した処理 を 実行
    protected abstract boolean runFunction(String inputCode);
}
Main.java
import java.io.*;
import java.util.*;

public class Main {
    public static void main(String[] args) {
        try {
            BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));

            // 処理を 選択して 実行
            AbstractDispatcher ad = new SelectFunction(reader);
            ad.run();

            reader.close();

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
// 処理を選択
class SelectFunction extends AbstractDispatcher {
    SelectFunction(BufferedReader reader) {
        super(reader);
    }

    // 選択肢 を 表示
    public void beforeDisplayMenu() {
        System.out.println("_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/");
        System.out.println("            人材管理システム");
        System.out.println("                メニュー");
        System.out.println("  [1].人材検索(S)");
        System.out.println("  [2].人材管理(JI:追加 JU:更新 JD:削除)");
        System.out.println("  [3].稼働状況管理(KI:追加 KD:削除)");
        System.out.println("  [4].終了(E)");
        System.out.println("_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/");
        System.out.println("どの機能を実行しますか?");
        System.out.print(" [S, JI, JU, JD, KI, KD, E]>");
    }

    // 選択した処理 を 実行
    public boolean runFunction(String inputCode) {
        if (inputCode.equals("S")) {
            // 人材検索処理
            AbstractDispatcher ad = new SelectSearchType(reader);
            ad.run();

        } else if (inputCode.equals("JI"))
            System.out.println("人材追加処理...\n");
        else if (inputCode.equals("JU"))
            System.out.println("人材更新処理...\n");
        else if (inputCode.equals("JD"))
            System.out.println("人材削除処理...\n");
        else if (inputCode.equals("KI"))
            System.out.println("稼働状況追加処理...\n");
        else if (inputCode.equals("KD"))
            System.out.println("稼働状況削除処理...\n");
        else 
            System.out.println("");

        return false;
    }
}
// 人材検索 検索方法を選択
class SelectSearchType extends AbstractDispatcher {
    SelectSearchType(BufferedReader reader) {
        super(reader);
    }

    // 選択肢 を 表示
    public void beforeDisplayMenu() {
        System.out.println("\n検索方法を指定してください。");
        System.out.println("N->氏名から検索");
        System.out.println("T->業種から検索");
        System.out.println("E->人材検索終了(メニューに戻る)");
        System.out.print(" [N, T, E]>");
    }

    // 選択した処理 を 実行
    public boolean runFunction(String inputCode) {
        // 業種一覧を作成
        Map<String, String> gyoshuMap = getGyoshuMap();

        if (inputCode.equals("N") || inputCode.equals("T")) {
            // 人材検索 呼び出し
            InputSearchStr ad = new InputSearchStr(reader);
            ad.run(inputCode, gyoshuMap);
        }

        return false;
    }

    // 業種一覧を作成
    private static Map<String, String> getGyoshuMap() {
        Map<String, String> gyoshuMap = new LinkedHashMap<String, String>();
        try {
            FileReader fr = new FileReader("gyoshu.txt");
            BufferedReader br = new BufferedReader(fr);
            String gyoshuStr;
            while ((gyoshuStr = br.readLine()) != null) {
                if (!gyoshuStr.endsWith("\t")) continue;
                String[] gyoshuAry = gyoshuStr.split( "\t");
                gyoshuMap.put(gyoshuAry[0], gyoshuAry[1]);
            }
            br.close();

        } catch (Exception e) {
            e.printStackTrace();
        }

        return gyoshuMap;
    }
}
// 人材検索 呼び出し
class InputSearchStr extends AbstractDispatcher {
    private String searchType;
    private Map<String, String> gyoshuMap;

    InputSearchStr(BufferedReader reader) {
        super(reader);
    }

    public void run(String searchType, Map<String, String> gyoshuMap) {
        this.searchType = searchType;
        this.gyoshuMap = gyoshuMap;

        super.run();
    }

    // 選択肢 を 表示
    public void beforeDisplayMenu() {
        if (searchType.equals("N")) {
            // 氏名で検索
            System.out.print("\n氏名に含まれる文字列を指定してください。\n>");
        } else if (searchType.equals("T")) {
            // 業種で検索
            System.out.println("\n業種を選択してください。");
            System.out.print(" [");
            Set gyoshuKeySet = gyoshuMap.keySet();
            Iterator i = gyoshuKeySet.iterator();
            while (i.hasNext()) {
                System.out.print(i.next());
                System.out.print(", ");
            }
            System.out.print("\b\b]>");
        }
    }

    // 終了?
    @Override
    protected boolean isEndCommand(String inputCode) {
        if (inputCode == null) return true;
        if (searchType.equals("N")) return false;
        return "E".equals(inputCode);
    }

    // 選択した処理 を 実行
    public boolean runFunction(String inputCode) {
        // 入力文字列で検索
        JinzaiKensaku ad = new JinzaiKensaku(reader);
        ad.run(searchType, inputCode, gyoshuMap);

        return true;
    }
}
// 人材検索
class JinzaiKensaku extends AbstractDispatcher {
    private int page;
    private String searchType;
    private String searchStr;
    private Map<String, String> gyoshuMap;

    JinzaiKensaku(BufferedReader reader) {
        super(reader);
    }

    public void run(String searchType, String searchStr, Map<String, String> gyoshuMap) {
        this.searchType = searchType;
        this.searchStr = searchStr;
        this.gyoshuMap = gyoshuMap;

        page = 0;

        super.run();
    }

    // 選択肢 を 表示
    public void beforeDisplayMenu() {
        // 人材一覧 表示
        page = jinzaiListDisplay(gyoshuMap, searchType, searchStr, page);

        // 選択肢 を 表示
        System.out.println("");
        System.out.println("P->前の10件");
        System.out.println("N->次の10件");
        System.out.println("E->検索一覧終了( 検索条件指定に戻る )");
        System.out.print(" [人材ID, P, N, E]>");
    }

    // 選択した処理 を 実行
    public boolean runFunction(String inputCode) {
        try {
            if (inputCode.equals("P"))
                page--;
            else if (inputCode.equals("N"))
                page++;
            else {
                // 人材情報 表示
                if (jinzaiDetailDisplay(gyoshuMap, inputCode)) {
                    System.out.print("エンターキーを押すと検索結果一覧に戻ります。\n>");
                    reader.readLine();
                }
                System.out.println("");
            }

        } catch (Exception e) {
            e.printStackTrace();
        }

        return false;
    }

    // 人材一覧 表示
    private int jinzaiListDisplay(Map<String, String> gyoshuMap, String searchType, String searchStr, int page) {
        try {
            while (true) {
                if (page < 0) page = 0;
                int min = page * 10;
                int max = min  +  9;
                boolean find = false;

                FileReader fr = new FileReader("jinzai.txt");
                BufferedReader br = new BufferedReader(fr);

                System.out.println("検索結果一覧");

                int cnt = -1;
                String jinzaiStr;
                while ((jinzaiStr = br.readLine()) != null) {
                    // 削除レコード
                    if (!jinzaiStr.endsWith("\t")) continue;

                    String[] jinzaiAry = jinzaiStr.split( "\t");

                    // 氏名で検索
                    if (searchType.equals("N") && (jinzaiAry[1].indexOf(searchStr) < 0 )) continue;
                    // 業種で検索
                    if (searchType.equals("T") && !jinzaiAry[9].equals(searchStr)) continue;

                    cnt++;
                    if (cnt < min) continue;
                    if (cnt > max) break;

                    find = true;

                    System.out.print(jinzaiAry[0]); // 人材ID
                    System.out.print("\t");
                    System.out.print(jinzaiAry[1]); // 人材名
                    System.out.print("\t");
                    System.out.println(gyoshuMap.get(jinzaiAry[9]));    // 業種ID
                }
                br.close();
                if (find) break;

                System.out.println("人材情報はありません。\n");
                if (page < 1) break;;
                page--;
            }

        } catch (Exception e) {
            e.printStackTrace();
        }

        return page;
    }
    // 人材情報 表示
    private boolean jinzaiDetailDisplay(Map<String, String> gyoshuMap, String jinzaiID) {
        boolean find = false;

        try {
            FileReader fr = new FileReader("jinzai.txt");
            BufferedReader br = new BufferedReader(fr);

            String jinzaiStr;
            while ((jinzaiStr = br.readLine()) != null) {
                // 削除レコード
                if (!jinzaiStr.endsWith("\t")) continue;

                String[] jinzaiAry = jinzaiStr.split( "\t");
                if (jinzaiAry[0].equals(jinzaiID)) {
                    System.out.println("");
                    System.out.println("人材ID : " + jinzaiAry[0]);
                    System.out.println("氏名 : " + jinzaiAry[1]);
                    System.out.println("郵便番号 : " + jinzaiAry[2]);
                    System.out.println("住所 : " + jinzaiAry[3]);
                    System.out.println("電話番号 : " + jinzaiAry[4]);
                    System.out.println("FAX番号 : " + jinzaiAry[5]);
                    System.out.println("e-mailアドレス : " + jinzaiAry[6]);
                    System.out.println("生年月日 : " + jinzaiAry[7]);
                    System.out.println("性別 : " + jinzaiAry[8]);
                    System.out.println("業種 : " + gyoshuMap.get(jinzaiAry[9]));
                    System.out.println("経験年数 : " + jinzaiAry[10] + "年");
                    System.out.println("最終学歴 : " + jinzaiAry[11]);
                    System.out.println("希望単価 : " + jinzaiAry[12] + "円");

                    // 稼働状況 表示
                    kadouListDisplay(jinzaiID);

                    find = true;
                    break;
                }
            }
            if (!find)
                System.out.println("入力された人材情報は登録されていません。");

            br.close();
        } catch (Exception e) {
            e.printStackTrace();
        }

        return find;
    }
    // 稼働状況 表示
    private void kadouListDisplay(String jinzaiID) {
        try {
            // 取引先一覧を作成
            Map<String, String> torihikiMap = getTorihikiMap();

            FileReader fr = new FileReader("kadou.txt");
            BufferedReader br = new BufferedReader(fr);

            System.out.println("\n稼働状況---------------------------------------------");

            String kadouStr;
            while ((kadouStr = br.readLine()) != null) {
                // 削除レコード
                if (!kadouStr.endsWith("\t")) continue;

                String[] kadouAry = kadouStr.split( "\t");
                if (!kadouAry[0].equals(jinzaiID)) continue;

                System.out.print(kadouAry[1]); // 稼働状況番号
                System.out.print("\t");
                System.out.print(kadouAry[3]); // 稼働開始日
                System.out.print("〜");
                System.out.print(kadouAry[4]); // 稼働終了日
                System.out.print("\t");
                System.out.println(torihikiMap.get(kadouAry[2])); // 取引先ID
            }
            br.close();

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    // 取引先一覧を作成
    private Map<String, String> getTorihikiMap() {
        Map<String, String> torihikiMap = new LinkedHashMap<String, String>();
        try {
            FileReader fr = new FileReader("torihiki.txt");
            BufferedReader br = new BufferedReader(fr);
            String torihikiStr;
            while ((torihikiStr = br.readLine()) != null) {
                if (!torihikiStr.endsWith("\t")) continue;
                String[] torihikiAry = torihikiStr.split( "\t");
                torihikiMap.put(torihikiAry[0], torihikiAry[1]);
            }
            br.close();

        } catch (Exception e) {
            e.printStackTrace();
        }

        return torihikiMap;
    }
}

これで、劇的にソースがすっきりすると思ったんだけど、
全然だめだ。。。

ryoasai さんのリファクタリング結果を参考に、少しずつ改善して行こう