Java Mid Basic〜Lv3_4_リファクタリングLv2 プロパティファイルで無修正ものを作る〜

イントロダクション

前回は、メインメソッドの修正を行わないでのコマンド追加実装をやりました。
具体的には、コンストラクタに作成した、クラスをマップに登録する処理を追加します(1行)。。。
しかし。これもなんだかんだとクラスの修正をしているのでイマイチです。なので

メインクラスを修正しない

この方向でコマンドを追加する方法を実践したいと思います。その方法は以下の手順で行います。

  1. プロパティファイルにキーとクラスの完全名をセットにして登録する
  2. クラスの完全名からクラスのインスタンスを取得して実行するようにする

上記のような形になります。「よくわからん。。。」と思った方、詳細を下に記載します。

修正のいらない実装

修正をしなくても「クラスを追加」もしくは、「新しい機能の実装を追加」するためには「依存関係を分断」してやる必要があります。

依存関係の分断

依存関係とは「関連性がある」ということ、つまり「関連性がある=片方を修正したら、もう片方も修正する必要がある」という関係が出来上がっていることです。
よく世間では「依存関係の注入」などという言葉で言いますが、実際どういうことか?に関して触れていることが少ないように感じています。
実装方法に関してはいろんな記事があり、わかりやすく説明がされていると思います。

つまり

依存関係を分断しようということです。具体的には下のように考えます。

  1. 新しい機能を追加するときには今実装している部分を修正したくない
  2. 新しく機能を追加するのと、実装中のクラスを修正しない方法は?

大雑把に、上のように考えます。サンプルとして今回のサンプルアプリを参考にして考えてみます。
現状の実装はGitにあるソースを見てもらうと一目瞭然です、一部抜粋して以下に示します。(メインメソッドのみ抜粋)

public static void main(String[] args) {
    // コマンドの用意
    cmdMap = new HashMap();
    cmdMap.put("hello", new HelloCommand());
    cmdMap.put("ready", new ReadyCommand());
    // 標準入力
    Scanner input = new Scanner(System.in);

    while(true) {
        System.out.println("入力してください: ");
        String inStr = input.nextLine();
        CommandIF cmd = cmdMap.get(inStr);
        if (cmd != null) {
            cmd.execute();
        }
        if ("bye".equals(inStr)) {
            System.out.println("Good Byw");
            break;
        }
    }
}

動きは下の動画のようになります。

この状態では、Mapにコマンドクラスを追加してやれば新しい機能を追加できます。なので修正するポイントはコンストラクタのみになります。

この際だから。。。

そう、「ここまできたら最後までいけ!」と思うのが人情(自分だけでしょうか?(笑))、なのでやってしまいましょう。

<実装手順>

  1. コンストラクタでMapにコマンドクラスを追加するのをやめる
  2. その代わりにプロパティファイルを使用する
  3. プロパティファイルから完全クラス名を取得し、インスタンスを生成
  4. 生成したインスタンスのメソッドを実行する

このようにしてやれば、メインクラスの修正はいりません。
<追加するもの>
・プロパティファイル
・プロパティファイルのロード処理
・同様に値の取得処理

[CommandList.properties]
 hello=jp.zenryoku.sample.lv3.refactor.cmd.HelloCommand
ready=jp.zenryoku.sample.lv3.refactor.cmd.ReadyCommand
/** プロパティファイル取得 */
public void loadPropertyFile() {
    prop = new Properties();
    try {
        // resources/
        prop.load(getClass().getResourceAsStream("/CommandList.properties"));
    } catch (IOException e) {
        e.printStackTrace();
        // エラーコード-1をセットしてプログラム終了
        System.exit(-1);
    }
}

プロパティファイルからインスタンスの取得

public CommandIF getCommandIF(String key) {
    // 完全クラス名を取得する
    String fullClassName = prop.getProperty(key);
    if (fullClassName == null || "".equals(fullClassName)) {
        return null;
    }
    CommandIF cmd = null;
    try {
        @SuppressWarnings("unchecked")
        Class cmdCls = (Class) Class.forName(fullClassName);
        cmd = cmdCls.newInstance();
    } catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
        System.exit(-1);
    }
    return cmd;
}

このように、メインメソッドの修正を行わなくてもCommandIFを実装したクラスの数だけコマンドに対する実行処理を作成することができます。

その代わり、プロパティファイルにキーと値を追加する必要がありますが。。。しかしコードを修正するとテストを行う必要があるので格段に余計な手間と時間を省くことができます。

まさに「無修正」

エロいですねぇ(笑)

できた時間を使用して、心と体のリフレッシュ及び、楽しい時間を過ごす。。。これが平和への第一歩だと思います。
このように、時間を大切に使わないと、昔の不況時代のように「寝る暇がない!」とか「今日も漫画喫茶でお泊まり!」とか。。。おおよそ人間の尊厳が無いような、不幸なことになってしまうので、日々精進を重ねることが大切だと思います。。。趣味にしてしまうのが一番手っ取り早いという噂もあるとか無いとか(笑)

でわでわ。。。