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

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

Step 17 その1 (処理フローの制御)

処理フローの制御を、入れ子構造にしてしまったせいで、非常に保守性が悪い。
自分でも、何故こんなことをしてしまったのか、わからない。
素直な構造に修正してみる。

処理フローを 制御する クラス
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();
            ad.run(reader, "");

            reader.close();

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

処理フローを 制御する 抽象クラス

abstract class AbstractDispatcher {
    protected BufferedReader     reader;
    protected boolean            hasEndCommand = true;
    protected Repository<Jinzai> jinzaiRepository;
    protected Jinzai             jinzai;

    public void setJinzaiRepository(Repository<Jinzai> jinzaiRepository) {
        this.jinzaiRepository = jinzaiRepository;
    }
    public void setJinzai(Jinzai jinzai) {
        this.jinzai = jinzai;
    }
    public Jinzai getJinzai() {
        return jinzai;
    }

    public String run(BufferedReader reader, String inputCode) {
        this.reader = reader;

        inputCode = "";

        while (true) {
            // 選択肢 を 表示
            displayMenu();

            // 処理 を 選択
            inputCode = acceptCode();

            // 終了?
            if (isEndCommand(inputCode)) return null;

            // 妥当な入力?
            if (!isValid(inputCode)) continue;

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

        // 状態 を 返す
        return inputCode;
    }

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

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

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

        return inputCode;
    }

    // 終了?
    private boolean isEndCommand(String inputCode) {
        if (inputCode == null) return true; // [CTR] + "Z" で 終了
        if (!hasEndCommand) return false;   // "E" で 終了しない場合
        return "E".equals(inputCode);       // "E" で 終了
    }

    // 妥当な入力?
    protected abstract boolean isValid(String inputCode);

    // 選択した処理 を 実行
    protected abstract boolean runFunction(String inputCode);
}

メニューを表示し、処理を選択させる

class SelectFunction extends AbstractDispatcher {
    // 選択肢 を 表示
    @Override
    protected void displayMenu() {
        System.out.println();
        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:追加 KU:更新 KD:削除)");
        System.out.println("  [4].終了(E)");
        System.out.println("_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/");
        System.out.println("どの機能を実行しますか?");
        System.out.print(" [S, JI, JU, JD, KI, KU, KD, E]>");
    }

    // 妥当な入力?
    @Override
    protected boolean isValid(String inputCode) {
        if (inputCode.equals("S")) return true;
        if (inputCode.equals("JI")) return true;
        if (inputCode.equals("JU")) return true;
        if (inputCode.equals("JD")) return true;
        if (inputCode.equals("KI")) return true;
        if (inputCode.equals("KU")) return true;
        if (inputCode.equals("KD")) return true;
        return false;
    }

    // 選択した処理 を 実行
    @Override
    protected boolean runFunction(String inputCode) {
        if (inputCode.equals("S")) {
            // 人材 検索処理
            jinzaiSearch(inputCode);
        } else if (inputCode.equals("JU")) {
            // 人材 更新処理
            jinzaiUpdate(inputCode);
        } else {
            System.out.println("");
        }

        return false; // メニューに 戻る
    }

人材 検索処理 の 処理フローを 制御

    private void jinzaiSearch(String inputCode) {
        Jinzai             search = new Jinzai();
        Jinzai             jinzai = new Jinzai();
        Repository<Jinzai> jinzaiRepository = new Repository<Jinzai>();

        String state = "検索方法指定";
        while(!state.equals("終了")) {
            if (state.equals("検索方法指定")) {
                // 検索方法を指定してください
                AbstractDispatcher ad = new JinzaiSearch();
                inputCode = ad.run(reader, inputCode);

                if (inputCode == null)
                    state = "終了";

                else if (inputCode.equals("N")) {
                    // 人材検索 呼び出し (氏名で検索)
                    state = "氏名入力";

                } else if (inputCode.equals("T")) {
                    // 人材検索 呼び出し (業種で検索)
                    state = "業種入力";
                }

            } else if (state.equals("氏名入力")) {
                // 氏名に含まれる文字列を指定してください
                AbstractDispatcher ad = new InputSearchStrByName();
                inputCode = ad.run(reader, inputCode);
                if (inputCode == null)
                    state = "検索方法指定";
                else {
                    search.setName(inputCode);
                    state = "人材検索(氏名)";
                }

            } else if (state.equals("業種入力")) {
                // 業種を選択してください
                AbstractDispatcher ad = new InputSearchStrByGyoshu();
                inputCode = ad.run(reader, inputCode);
                if (inputCode == null)
                    state = "検索方法指定";
                else {
                    search.setGyoshuID(inputCode);
                    state = "人材検索(業種)";
                }

            } else if (state.equals("人材検索(氏名)") || state.equals("人材検索(業種)")) {
                if (state.equals("人材検索(氏名)"))
                    jinzaiRepository.select("jinzai.txt", false, new JinzaiMatcherByName(search), Jinzai.class);
                else if (state.equals("人材検索(業種)"))
                    jinzaiRepository.select("jinzai.txt", false, new JinzaiMatcherByGyoshu(search), Jinzai.class);
                
                // 検索結果一覧
                AbstractDispatcher ad = new JinzaiKensaku();
                ad.setJinzaiRepository(jinzaiRepository);
                ad.setJinzai(jinzai);
                inputCode = ad.run(reader, inputCode);
                if (inputCode == null)
                    state = "検索方法指定";
            }
        }

人材 更新処理 の 処理フローを 制御

    private void jinzaiUpdate(String inputCode) {
        Jinzai             search = new Jinzai();
        Jinzai             jinzai = new Jinzai();
        Repository<Jinzai> jinzaiRepository = new Repository<Jinzai>();
        boolean isDirty = false;

        String state = "人材ID入力";
        while(!state.equals("終了")) {
            if (state.equals("人材ID入力")) {
                // 人材IDを入力してください
                isDirty = false;
                AbstractDispatcher ad = new JinzaiUpdate();
                jinzaiRepository.select("jinzai.txt", false, new Matcher(null), Jinzai.class);
                ad.setJinzaiRepository(jinzaiRepository);
                ad.setJinzai(jinzai);
                inputCode = ad.run(reader, inputCode);
                jinzai = ad.getJinzai();

                if (inputCode == null)
                    state = "終了";
                else
                    state = "更新項目選択";

            } else if (state.equals("更新項目選択")) {
                // 更新項目を選択してください
                AbstractDispatcher ad = new SelectUpdateField();
                ad.setJinzai(jinzai);
                inputCode = ad.run(reader, inputCode);
                jinzai = ad.getJinzai();

                if (inputCode == null) {
                    if (isDirty)
                        state = "更新処理";
                    else
                        state = "人材ID入力";
                } else
                        state = "更新値入力";

            } else if (state.equals("更新値入力")) {
                // 更新後の値を入力
                AbstractDispatcher ad = new InputUpdateValue();
                ad.setJinzai(jinzai);
                inputCode = ad.run(reader, inputCode);
                jinzai = ad.getJinzai();

                if (inputCode == null) {
                    if (isDirty)
                        state = "更新処理";
                    else
                        state = "人材ID入力";
                } else {
                    isDirty = true;
                    state = "更新項目選択";
                }

            } else if (state.equals("更新処理")) {
                DataViewer dataViewer = new JinzaiDetailView();
                UpdateConfirmBase<Jinzai> updateConfirm = new UpdateConfirm<Jinzai>();
                inputCode = updateConfirm.run(reader, Jinzai.class, jinzai, dataViewer, "jinzai.txt", new JinzaiMatcher(jinzai));

                if (inputCode == null)
                    state = "人材ID入力";
                else if (inputCode.equals("Y")) {
                    // 「以下の内容で更新します。」で "Y" なら 「人材IDを入力」に 戻る、 
                    state = "人材ID入力";
                } else if (inputCode.equals("N")) {
                    // 「以下の内容で更新します。」で "N" なら 「更新したい項目を選択してください」に戻る
                    state = "更新項目選択";
                }
            }
        }
    }