Java クラスの扱い〜役割分担をする〜

イントロダクション

オブジェクト指向の理解をするのに、以下の様なステップを踏むと理解しやすいであろうというものです。

クラスの役割分担をする

以前作成したJava ミニゲーム ソース 〜じゃんけんゲーム in Console 〜を作るのにクラスの役割分担を考えます。

単純に以下の様な分担を思いつくかもしれません。

  • メインメソッドを持つクラス
  • じゃんけんゲームを起動するクラス
  • じゃんけんの入力やCPUの手を取得などのユーティリティクラス
  • 入力チェッククラス
  • コンソール表示を行うクラス

全部で5クラスを使うアイデアが出ました。人によりアイデアは違うのでどんな分担がベストなものか議論してみるのも楽しいかもしれません。

## ソースを眺めてみる
[上記のリンク先](https://zenryokuservice.com/wp/2020/06/12/java-%e3%83%9f%e3%83%8b%e3%82%b2%e3%83%bc%e3%83%a0-%e3%82%bd%e3%83%bc%e3%82%b9-%e3%80%9c%e3%81%98%e3%82%83%e3%82%93%e3%81%91%e3%82%93%e3%82%b2%e3%83%bc%e3%83%a0-in-console-%e3%80%9c/)は下のようなクラス構成になっています。
* **Mainクラス**:メインメソッドを持っているクラス
- 「exe」と入力するとFirstCls#execute()が起動する
* **FirstClsクラス**:各処理を実装しているクラス

上記のリンク先で作成したものは、ちょっと面倒な起動の仕方をしています。じゃんけんゲーム他にも何か実装しようとしたためです。

## 役割分担の効果
ここで我々人間がみんなで作業をするときに行う「役割分担」をプログラム上で行うことを考えて見ます。
上のように、「メインメソッドを起動するクラス」と「各処理を実装しているクラス」を作成して、**作業を分担**しました。

## 分担することでできること1
### <インターフェースの追加実装>
これは、単純に分けただけですが、メインメソッドの実装をしたのように変更したとします。 ※FirstClsの実装も変える必要があります。
**ExeInterface**を作成し、FirstClsクラスに実装(implements)します
<作成するインターフェース>

public interface ExeInterface {
    // 抽象メソッド(implementsしたクラスに実装を強制する)
    public execute(Scanner scan);
}

<FirstClsにimeplementsする>

public class FirstCls implements ExeInterface {
    // 中身は省略
}
public class Main {
    public static void main(String[] args) {
        Scanner scan = new Scanner(System.in);
        String input = scan.next();

        ExeInterface first = new FirstCls();
        if ("exe".equals(input)) {
            first.execute(scan);
        }
        boolean isNumber = first.isNumberString(input);
        if (isNumber) {
            System.out.println(input + "は数字です。");
        } else {
            System.out.println(input + "は数字ではありません。");
        }
    }
}

このような形にすることで、「FirstClsのじゃんけんゲームではなく、別のゲームを起動したい」と思った時に、FirstClsと同様に「ExeInterfaceを実装」してSecondClsを作成したとします。
メインメソッドも下のように修正します。

public class Main {
    public static void main(String[] args) {
        Scanner scan = new Scanner(System.in);
        String input = scan.next();

        ExeInterface second = new SecondCls();
        if ("exe".equals(input)) {
            second.execute(scan);
        }
        boolean isNumber = first.isNumberString(input);
        if (isNumber) {
            System.out.println(input + "は数字です。");
        } else {
            System.out.println(input + "は数字ではありません。");
        }
    }
}

このように修正することで、SecondCls#execute()を実行できるようになります。ちなみに修正した部分は2行です。

## さらに、ポリモーフィズムする
インターフェースを作成したことで、簡単に起動するクラスを変更できるようになりました。
しかし、メインメソッドをいちいち修正するのも面倒なので、動的に起動するクラスを追加できるようにしたいと思います。
同じような実装として[「CommandIF」というインターフェースを作成して、使用したときの記事](https://zenryokuservice.com/wp/2020/10/05/java-basic-%e3%80%9c%e3%82%a4%e3%83%b3%e3%82%bf%e3%83%bc%e3%83%95%e3%82%a7%e3%83%bc%e3%82%b9%e3%81%ae%e6%89%b1%e3%81%84%e6%96%b9%ef%bc%92%e3%80%9c/)もありますので参考までにどうぞ。

### ここでの問題1
**メインメソッドの中で「new XXXX」というコードを書くと、起動するクラスを動的に変更できない。**

という問題がありますので、この部分を動的に変更できるようにします。テクノロジーとしては「リフレクション」というものを使用します。Javaパッケージとしては「[java.lang.refrect](https://docs.oracle.com/javase/jp/6/api/java/lang/reflect/package-summary.html)」になります。

#### 具体的に。。。
1. 起動するクラスを動的に変更するために入力によって起動するクラスを取得するように修正
2. 入力値をキーにして、取得するクラスの完全修飾名を取得する
3. 完全修飾名より、起動するクラスのインスタンスを生成、取得する

大まかに上記のような手順で実装します。
##### 1. 起動するクラスを動的に変更するために入力によって起動するクラスを取得するように修正
これは単純に入力値によって条件分岐すれば良いです。

##### 2. 入力値をキーにして、取得するクラスの完全修飾名を取得する
キーと値をセットで動的に取得する、ということを考えると「プロパティファイル」を使用すると楽です。
コードとしては、下のように実装すると、プロパティファイルを読み取ることができ、プロパティファイルは追加したいクラスのキーと値をセットにして追記してやれば良いです。

<プロパティファイルの例>

first=jp.zenryoku.sample.FirstCls
second=jp.zenryoku.sample.SecondCls
third=jp.zenryoku.sample.ThirdCls

<プロパティファイルを読み取る例>

/** コンストラクタ */
public Lv3_1_RefactorLv2() {
    commandList = new ArrayList<String>();
    prop = new Properties();
    // 現在位置の状態を保持するマップ
    placeInfo = getInfoMap();
    try {
        // resources/
        prop.load(getClass().getResourceAsStream("/test.properties"));
    } catch (IOException e) {
        e.printStackTrace();
        // エラーコード-1をセットしてプログラム終了
        System.exit(-1);
    }
}

<プロパティファイルのキーから値を取得する例>

/**
 * プロパティファイルから値が取得できた、値を返し
 * 取得できない時はから文字("")を返す
 * @param inStr キー(コマンド)
 * @return プロパティファイルの値
 */
public String getPropertes(String inStr) {
    String value = prop.getProperty(inStr);
    if ("".equals(value)) {
        listPropertyKeys();
        return "";
    }
    return value;
}

上のようなメソッドを実装して、プロパティファイルのキーから値(クラスの完全修飾名)を取得します。

##### 3. 完全修飾名より、起動するクラスのインスタンスを生成、取得する
このサンプルコードは、インターフェースとして作成した「CommandIF」を使用していますが、他のインターフェースの場合は「CommandIF」を他のインターフェース型に変更してやれば良いです。

#### リフレクションの実装
そして、ポイントになるのは

Class.forName(fullClassName);

の部分です、リフレクションの実装になります。クラスの完全名からクラスオブジェクトを取得、インスタンスの生成。というような処理を行います。

CommandIF cmd = null;
try {
    @SuppressWarnings("unchecked")
    Class<CommandIF> cmdCls = (Class<CommandIF>) Class.forName(fullClassName);
    cmd = cmdCls.newInstance();
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
    System.exit(-1);
}

ポイントとしては、完全修飾名の「fullClsName」を引数にクラスオブジェクトを取得(Class#forName())してからインスタンスを生成(Class#newInstance())しているところです。

この処理で生成したクラスを返却してやれば、動的に「CommandIF」を実装したクラスを実行することができます。

## まとめ
役割分担すると「ポリモーフィズム」が使いやすいということです。

<サンプル動画>

## 関連ページ一覧

Eclipse セットアップ

  1. Java Install Eclipse〜開発ツールのインストール〜
  2. TensorFlow C++環境〜EclipseCDTをインストール〜
  3. Setup OpenGL with JavaJOGLを使う準備 for Eclipse
  4. Eclipse Meven 開発手順〜プロジェクトの作成〜
  5. Java OpenCV 環境セットアップ(on Mac)
  6. Eclipse SceneBuilderを追加する
  7. JavaFX SceneBuilder EclipseSceneBuilder連携~

Java Basic一覧

  1. Java Basic Level 1 〜Hello Java〜
  2. Java Basic Level2 〜Arithmetic Calculate〜
  3. Java Basic Level3 〜About String class〜
  4. Java Basic Level 4〜Boolean〜
  5. Java Basic Level 5〜If Statement〜
  6. Java Basic Summary from Level1 to 5
  7. Java Basic Level 6 〜Traning of If statement〜
  8. Java Basic Level8 〜How to use for statement〜
  9. Java Basic Level 8.5 〜Array〜
  10. Java Basic Level 9〜Training of for statement〜
  11. Java Basic Level 10 〜While statement 〜
  12. Java Basic Swing〜オブジェクト指向〜
  13. Java Basic Swing Level 2〜オブジェクト指向2〜
  14. サンプル実装〜コンソールゲーム〜
  15. Java Basic インターフェース・抽象クラスの作り方
  16. Java Basic クラスとは〜Step2_1〜
  17. Java Basic JUnit 〜テストスイートの作り方〜

Git関連

  1. Java Git clone in Eclipse 〜サンプルの取得〜
  2. Eclipse Gitリポジトリの取得 GitからソースをPullしよう〜
  3. IntelliJ IDEA GitGitリポジトリからクローン〜

## JavaFX関連ページ
1. [Eclipse SceneBuilderを追加する](https://zenryokuservice.com/wp/2018/11/17/eclipse-scenebuilder%e3%82%92%e8%bf%bd%e5%8a%a0%e3%81%99%e3%82%8b/)
1. [JavaFX SceneBuilder 〜EclipseとSceneBuilder連携~](https://zenryokuservice.com/wp/2018/11/17/javafx-scenebuilder-%e3%80%9ceclipse%e3%81%a8scenebuilder%e9%80%a3%e6%90%ba/)
1. [JavaFX SceneBuilder〜ボタンにメソッドを割り当てるワンポイント〜](https://zenryokuservice.com/wp/2019/02/05/javafx-scenebuilder%e3%80%9c%e3%83%9c%e3%82%bf%e3%83%b3%e3%81%ab%e3%83%a1%e3%82%bd%e3%83%83%e3%83%89%e3%82%92%e5%89%b2%e3%82%8a%e5%bd%93%e3%81%a6%e3%82%8b%e3%83%af%e3%83%b3%e3%83%9d%e3%82%a4%e3%83%b3/)
1. [Java プロコンゲーム 〜見た目の作成(SceneBuilderの使用)〜](https://zenryokuservice.com/wp/2020/03/30/java-%e3%83%97%e3%83%ad%e3%82%b3%e3%83%b3%e3%82%b2%e3%83%bc%e3%83%a0-%e3%80%9c%e8%a6%8b%e3%81%9f%e7%9b%ae%e3%81%ae%e4%bd%9c%e6%88%90scenebuilder%e3%81%ae%e4%bd%bf%e7%94%a8%e3%80%9c/)

## ステップアップ関連ページ一覧

  1. Java 初めてでも大丈夫〜ステップアッププログラミングのススメ〜
  2. ステップアッププログラミング〜Java FxでHelloWorld解説〜
  3. Java StepUpPrograming〜JavaFX で四則計算〜
  4. Java StepUpPrograming〜JavaFXで画面切り替えを作る1〜
  5. Java StepUpPrograming〜JavaFXで画面切り替え2ボタン作成〜
  6. Java StepUpPrograming〜JavaFXで画面切り替え3アクション〜
  7. Java StepUpPrograming〜JavaFXで画面切り替え4Pane切り替え〜
  8. Java StepUpPrograming〜JavaFXで画面切り替え5WebEngine

## JavaFX + ND4Jで機械学習準備

  1. JavaFX + ND4J〜数学への挑戦1:ND4Jのインストール〜
  2. JavaFX + ND4J〜数学への挑戦2: 行列の計算〜
  3. Java + ND4J 〜数学への挑戦3: ベクトル(配列)の作成方法〜

## オブジェクト指向関連ページ
1. [オブジェクト指向の概念1〜OracleDocのチュートリアル1〜](https://zenryokuservice.com/wp/2019/10/301. /%e3%82%aa%e3%83%96%e3%82%b8%e3%82%a7%e3%82%af%e3%83%88%e6%8c%87%e5%90%91%e3%81%ae%e6%a6%82%e5%bf%b5-%e3%80%9coracledoc%e3%81%ae%e3%83%81%e3%83%a5%e3%83%bc%e3%83%88%e3%83%aa%e3%82%a2%e3%83%ab%ef%bc%91/)
1. [オブジェクト指向の概念2〜クラスとは〜](https://zenryokuservice.com/wp/2019/10/30/%e3%82%aa%e3%83%96%e3%82%b8%e3%82%a7%e3%82%af%e3%83%88%e6%8c%87%e5%90%91%e3%81%ae%e6%a6%82%e5%bf%b5%e3%80%9c%e3%82%af%e3%83%a9%e3%82%b9%e3%81%a8%e3%81%af%e3%80%9c/)

Java Discord

  1. IntelliJ IDEA Discord Botを作る〜Gradle環境のセットアップ〜
  2. Java Discord セットアップ〜Hello Discord〜
  3.  Java Discord ピンポン〜Discordプログラム〜
  4. Java Discord Listener実装〜コマンドを好きなだけ追加しよう〜

## 設計
1. [設計を始める〜1.アプリイメージ〜](https://zenryokuservice.com/wp/2018/10/25/%e8%a8%ad%e8%a8%88%e3%82%92%e5%a7%8b%e3%82%81%e3%82%8b/)
1. [設計を始める〜2.機能イメージ〜](https://zenryokuservice.com/wp/2018/10/25/%e8%a8%ad%e8%a8%88%e3%82%92%e5%a7%8b%e3%82%81%e3%82%8b%e3%80%9c2-%e6%a9%9f%e8%83%bd%e3%82%a4%e3%83%a1%e3%83%bc%e3%82%b8%e3%80%9c/)
1. [設計を始める〜3.機能概要〜](https://zenryokuservice.com/wp/2018/10/26/%e8%a8%ad%e8%a8%88%e3%82%92%e5%a7%8b%e3%82%81%e3%82%8b%e3%80%9c3-%e6%a9%9f%e8%83%bd%e6%a6%82%e8%a6%81%e3%80%9c/)
1. [Java はじめて16 〜クラス設計から実装〜](https://zenryokuservice.com/wp/2019/09/19/java-%e3%81%af%e3%81%98%e3%82%81%e3%81%a616-%e3%80%9c%e3%82%af%e3%83%a9%e3%82%b9%e8%a8%ad%e8%a8%88%e3%81%8b%e3%82%89%e5%ae%9f%e8%a3%85%e3%80%9c/)