Java Basic 中級編 ~③メインメソッドを修正しないで拡張する~

イントロダクション

前回は、メインメソッドを修正しない形での実装を行いました。

この段階では、まだ役割分担を行っただけでこれがどんな効果があるのか?というところがわからないと思います。

なので、今回は、「〇×あてゲーム」を拡張することを考えていきたいと思います。
作成したプログラムコードはGithubにアップしてあります。

メインメソッドを修正しないで拡張する

今までに「役割分担」を行ったので、この役割分担を活用していきます。
Lv2Mainクラスはメインメソッドを動かすクラスなので、これ以外のクラスを実装していきます。

  • Lv2Main
  • MarubatsuConsole
  • MarubatsuUtils

Lv2Mainを見る

まずは、メインメソッドを見てみます。Lv2Main

/**
 * Javaの基本レベル2:小さなレベルのプログラムを拡張する。
 * 今回は、〇か×か当てるゲーム。
 */
public class Lv2Main {
    /** 終了フラグ */
    private static boolean isFinish;

    public static void main(String[] arg) {
        isFinish = false;
        // 標準入力を受け取るクラスをインスタンス化
        Scanner scan = MarubatsuUtils.getScanner();

        while (true) {
            // ゲーム開始文言
            MarubatsuConsole.printGameStart();
            // 標準入力を受け取る
            int input = scan.nextInt();
            // isFinishがtrueならば処理終了。
            if (isFinish) {
                MarubatsuConsole.printTerminated();
                break;
            }
            // 0か1の値を返却する
            int res = MarubatsuUtils.nextInt(2);

            // 0: 〇 1: ×で当たったかどうかの判定
            boolean isAtari = MarubatsuUtils.judgeAtariOrNot(res, input);
            MarubatsuConsole.printAtatiorNot(isAtari, input);

            // 続けるのかどうか判定する
            if (MarubatsuConsole.printNextPlayOrNot(scan)) {
                break;
            }
        }
    }
}

コメントを並べただけですが、以下の順序で処理を行っています。

  1. 標準入力を受け取るクラスをインスタンス化
  2. ゲーム開始文言
  3. 標準入力を受け取る
  4. isFinishがtrueならば処理終了。
  5. 0か1の値を返却する
  6. 0: 〇 1: ×で当たったかどうかの判定
  7. 続けるのかどうか判定する

上の番号で行くと2~7がループ処理の中にあります。
なので、7の「続けるか判定する」部分でNo(続けない)を選択するまで無限ループします。

インスタンス化

Boolean

標準入力

フィールド変数の扱い

拡張ポイントを見つける

そして、役割分担を行ったところ、つまりは、下のクラスを使用している部分が拡張ポイントになります。
Githubにアップしてあります。

具体的には、以下のコメント部分です。

  • ゲーム開始文言
  • isFinishがtrueならば処理終了。の終了表示部分
  • 0か1の値を返却する
  • 0: 〇 1: ×で当たったかどうかの判定
  • 続けるのかどうか判定する

もしも、他の部分を拡張したいと思たのならば、メインメソッドを少し修正する必要があります。
何かの処理をほかのクラスに任せてある状態(MarubatsuConsole, MarubatsuUtilsを使用している状態)ならば
メインメソッドを修正する必要がありません。
しかし、直接メインメソッドを修正する必要がある場合はやはり、修正する必要があります。

このような実装方法を理解すると、下のようなプログラムが作れます。

※ビルドするのに3分くらい時間がかかっています。
このプログラムは、メインの処理部分も、別クラスにしているので、まったく別のプログラムを起動するように修正することもできます。
しかし、現状では、テキストRPGを作成するところに注力しているので役割分担を行いそれぞれの役割を拡張して実装しています。
具体的には、テキストファイルを読み、それをもとにデータ(ステータスやアイテム)を生成してそれをゲームの中で使用する形での実装を行っております。
ちょっと残骸が残っていますが。。。

拡張するとき

先に示したように、別のクラスを呼び出している(使用している)部分を拡張、つまり処理を追加することができるので、次の部分を拡張することができます。

  • ゲーム開始文言
  • isFinishがtrueならば処理終了。の終了表示部分
  • 0か1の値を返却する
  • 0: 〇 1: ×で当たったかどうかの判定
  • 続けるのかどうか判定する

具体的には、ゲーム開始文言を変更することができる。とか、当たったかどうかの履歴を付ける。とか
そこらへんは、実装者のアイディア次第でどこまでも広げることができます。

ゲームの開始文言を変更する

先ほど、拡張する例としてゲームの開始文言を変更するということを上げました。これを実際にやってみます。

やっている手順としては、以下の通りです。

  1. JUnitテストケースの作成
  2. 現状のプログラム実行確認
  3. プログラムの修正、実行確認(目視で確認)

最後の実行確認(目視で確認)に関しては、すべてプログラムで確認することができます。
そのためには、ちょっと面倒なことをする必要があるので、割愛しました。
具体的には標準出力の出力先を変更して(別のPrintStreamを使用)そのストリーム内の出力内容と期待値を比較するという形になります。

現状としては、単純に〇×あてゲームの初期表示文言を変更するだけなので、目視で確認しました。

0: 〇 1: ×で当たったかどうかの判定

この部分は上記の動画では、挙動がおかしくなっていました。なので、これを修正し想定通りの実行結果が得られるようにプログラムを修正します。

実行した結果は下のようになります。

******************************
*「〇×あてゲーム」 0: 〇 1: ×。 *
******************************
※0は「〇」を表し1は「×」を表します。
1
はずれ:×
続けますか? 0: 続ける  1: やめる
0
******************************
*「〇×あてゲーム」 0: 〇 1: ×。 *
******************************
※0は「〇」を表し1は「×」を表します。
1
はずれ:×
続けますか? 0: 続ける  1: やめる
1

その前に、問題点を明確にします。

今回の問題点は「あたりのときの値、つまりは、『〇』が当たりなのか?『×』が当たりなのか?」が明確でないというところです。
なので、これも修正します。

先ほどと同じようにまずは、修正ポイント(先ほどは拡張ポイントと記載しました。)を見つけます。
実行結果を見ると、「はずれ:×」のように当たりはどちらなのか?がわからない状態ですので、これを明確にします。

具体的には、「「〇」があたりです。「×」が当たりですなどの文言を表示するように修正」する形で対応しようと考えております。

当然、他に良いアイディアがあれば、それを実装するとよいと思います。

当たり判定処理の修正

今回作成した「当たり判定処理」は、MarubatsuUtils#judgeAtariOrNotで実装しているのでこれを修正します。

現状のプログラムは下のようになっています。

    /**
     * 生成した乱数と、入力値が等しいか判定する。
     *
     * @param res 生成した乱数
     * @param input 入力値
     * @return true: 等しい false: 違う
     */
    public static boolean judgeAtariOrNot(int res, int input) {
        // 0: 〇 1: ×で当たったかどうかの判定
        return res == input;
    }

このプログラムの問題点は、〇と×のどちらが当たりなのか表示されない点です。
なので、これを表示するようにプログラムを修正する必要があります。

テストケースを作成する

先ほど初期表示の文言を表示するテストケースを作成しました。
これに追加して、「〇と×のどちらが当たりなのか表示する」テストケースを実装します。

仕様から考える

今回の要件(どのように動いたらよいか?)は「〇と×のどちらが当たりなのか表示する」ということです。
これを確かめるプログラムを考えます。

確認項目をリストアップ

  1. あたりは「〇」「×」どちらか表示する
  2. 予想を入力したユーザーの入力は「〇」「×」どちらか表示する

簡単ですが、2項目になります。

これもテストケースを作成する

ちなみに、このクラスのテストケースは以前、他のテストケースを作成していたので、これに今回のテストケースを追加します。

JUnitのセットアップ方法

テストケースの作り方①

テストケースの作り方②

テスト実行

サンプル動画

やったことは、同じです。

  1. 現状のプログラム実行確認
  2. プログラムの修正
  3. プログラムの実行確認

ちょっと長めの動画になりましたが、行ったことをそのまま動画にしてあります。
ポイントとしては、テストケースのプログラムの実装、作成方法、考え方(この記事に記載)を実際に行った
動画にしてあります。
別な言い方をすると、自分がこの作業を行ったものを動画にしました。

でわでわ。。。

<<< 前回

Java Basic 中級編 ~②メインメソッドを修正しない形を作る~

イントロダクション

前回アプリケーションを作り、それを運用すること、拡張することについて考えてみました。

まとめると次のようなことが必要になるということを記載しました。

  • 各プログラム間(Javaのクラス同士)の依存度を限りなく低くする。
  • プログラムを拡張するのに、元のプログラムをほとんど変更しない。
  • Javaを使えるレベルの知識(技術)がある人なら、誰が見てもわかるようなコードを書く。
  • 各クラス(部品)の単体テスト(UnitTest)ケースを用意しておき、修正したら即テスト、部品を取り換えるだけでよいように、作成した資源(プログラムのコード)を結合テスト・総合テストと実施できるような体制を整える

今回は、プログラムを拡張していくための基本になる考え方と実践方法について記載していきたいと思っています。
例として「〇×あてゲーム」を拡張していく形でプログラム(クラス)間の依存度を低くした形のプログラミング方法について記載していきたいと思います。

コードを修正しなくても良い形を作る

前回作成したプログラムは下のものになります。

public class Lv2Main {
    /** 終了フラグ */
    private static boolean isFinish;

    public static void main(String[] arg) {
        isFinish = false;
        // 標準入力を受け取るクラスをインスタンス化
        Scanner scan = new Scanner(System.in);

        while (true) {
            System.out.println("「〇×あてゲーム」 0: 〇 1: ×。");
            // 標準入力を受け取る
            int input = scan.nextInt();
            // isFinishがtrueならば処理終了。
            if (isFinish) {
                System.out.println("プログラムを終了します。");
                break;
            }
            // 0か1の値を返却する
            Random rdm = new Random();
            int res = rdm.nextInt(1);

            // 0: 〇 1: ×で当たったかどうかの判定
            boolean isAtari = res == input;
            String value = input == 0 ? "〇" : "×";
            if (isAtari) {
                System.out.println("あたり:" + value);
            } else {
                System.out.println("はずれ:" + value);
            }
            System.out.println("続けますか? 0: 続ける  1: やめる");
            int next = scan.nextInt();
            if (next == 1) {
                break;
            }
        }
    }
}

この状態のプログラムはよく見かける。。。と思われる形のプログラムです。
つまりは、処理を1つのファイル内にすべて書いている形のプログラムということです。

このブログ的に表現すると「Javaの基本:上巻」に当たるプログラムの書き方になります。

「Javaの基本:下巻」に当たるプログラムの書き方はこれから説明していきます。

Step1. 役割分担を行う

上のプログラムで行っていることを、例えばチームで作業をするように、役割分担を行いそれぞれのクラスにそれぞれの役割を与えます。

例えば、次のような役割分担を行います。

  1. 〇×あてゲームを起動する役割
  2. 標準入力を受け取る、などの標準入出力をコントロールする役割
  3. 〇×あてゲームの各種判定を行う役割

もともとのプログラムは、全部の処理が書いてあるので、これを切り貼りして改造します。

Step2. クラスを作成する

上記の通り、2つの役割を担当するクラスを作成します。

役割分担を確認

クラス名 役割
Lv2Main ※作成済み 〇×あてゲームを起動する役割
MarubatsuConsole 標準入力を受け取る、などの標準入出力をコントロールする役割
MarubatsuUtils 〇×あてゲームの各種判定、ユーティリティの提供を行う役割

これらのクラスに必要な処理を切り貼りして、各クラスに移植します。具体的には、作成したコードを切り貼りして、それぞれの役割のクラス内のメソッドに移植します。

その結果を以下に記載致します。
※リンクはGithubにアップしたコードです。

Lv2Main>: 〇×あてゲームを起動

package jp.zenryoku.tutorial.level2;

import java.util.Scanner;

/**
 * Javaの基本レベル2:小さなレベルのプログラムを拡張する。
 * 今回は、〇か×か当てるゲーム。
 */
public class Lv2Main {
    /** 終了フラグ */
    private static boolean isFinish;

    public static void main(String[] arg) {
        isFinish = false;
        // 標準入力を受け取るクラスをインスタンス化
        Scanner scan = MarubatsuUtils.getScanner();

        while (true) {
            // ゲーム開始文言
            MarubatsuConsole.printGameStart();
            // 標準入力を受け取る
            int input = scan.nextInt();
            // isFinishがtrueならば処理終了。
            if (isFinish) {
                MarubatsuConsole.printTerminated();
                break;
            }
            // 0か1の値を返却する
            int res = MarubatsuUtils.nextInt(2);

            // 0: 〇 1: ×で当たったかどうかの判定
            boolean isAtari = MarubatsuUtils.judgeAtariOrNot(res, input);
            MarubatsuConsole.printAtatiorNot(isAtari, input);

            // 続けるのかどうか判定する
            if (MarubatsuConsole.printNextPlayOrNot(scan)) {
                break;
            }
        }
    }
}

MarubatsuConsole>:標準入出力をコントロール

※修正した後のコードがアップしてあります。

package jp.zenryoku.tutorial.level2;

import java.util.Scanner;

public class MarubatsuConsole {
    /**
     * 〇×あてゲームの開始文言を表示
     */
    public static void printGameStart() {
        System.out.println("「〇×あてゲーム」 0: 〇 1: ×。");
    }

    /**
     * 〇×あてゲームの終了文言を表示
     */
    public static void printTerminated() {
        System.out.println("プログラムを終了します。");
    }

    /**
     * 当たったかどうかを表示する。
     * @param isAtari 当たり判定の結果
     */
    public static void printAtatiorNot(boolean isAtari, int input) {
        String value = input == 0 ? "〇" : "×";
        if (isAtari) {
            System.out.println("あたり:" + value);
        } else {
            System.out.println("はずれ:" + value);
        }
    }

    /**
     * 〇×あてゲームを続けるかを表示、Yes or Noを取得する
     * @param scan
     * @return true: 続ける  false: やめる
     */
    public static boolean printNextPlayOrNot(Scanner scan) {
        boolean playNext = false;
        System.out.println("続けますか? 0: 続ける  1: やめる");
        int next = scan.nextInt();
        if (next == 1) {
            playNext = true;
        }
        return playNext;
    }
}

MarubatsuUtils>: ユーティリティの提供を行う役割

つまるところは、「便利機能を提供するクラス」ということです。
※修正した後のコードがアップしてあります。

package jp.zenryoku.tutorial.level2;

import java.util.Random;
import java.util.Scanner;

public class MarubatsuUtils {
    /** 標準入力を受け取るクラス */
    private static Scanner scan;
    /** 乱数の生成クラス */
    private static Random rdm;

    /**
     * 標準入力を受け取るクラスを生成、取得する。
     * ※シングルトン実装
     * @return Scanner
     */
    public static Scanner getScanner() {
        if (scan == null) {
            scan = new Scanner(System.in);
        }
        return scan;
    }

    /**
     * 乱数生成クラスがインスタンス化されていなければ、インスタンス化します。
     * すでにインスタンス化しているときは、既存のインスタンスを使用します。
     * ※シングルトン実装
     *
     * @param bound
     * @return 生成した乱数
     * @see <a href="https://docs.oracle.com/javase/jp/8/docs/api/java/util/Random.html">Random</a>
     */
    public static int nextInt(int bound) {
        if (rdm == null) {
             rdm = new Random();
        }
        return rdm.nextInt(bound);
    }

    /**
     * 生成した乱数と、入力値が等しいか判定する。
     *
     * @param res 生成した乱数
     * @param input 入力値
     * @return true: 等しい false: 違う
     */
    public static boolean judgeAtariOrNot(int res, int input) {
        // 0: 〇 1: ×で当たったかどうかの判定
        return res == input;
    }

}
  • メインメソッドは、すっきりしたコードになったと思います。
  • メインメソッドでは、どんな処理をしているかが一目瞭然になり。各クラスはその処理だけが書いてある形になります。

もちろん、引数と返り血があるので、呼び出し元に多少なりとも影響が出ます。

しかし、これでメインメソッドを変更しなくても〇×あてゲームを拡張する準備ができました。

ポイント1

これでプログラムの修正するときには、それぞれのクラス、メソッドを修正してやればよくなりました。
メインメソッドは修正する必要がなくなったということです。

ポイント2

メインメソッドには、大まかな処理の順序を書く。具体的には下のような形です。

// <無限ループ開始>
// ■ゲーム開始文言を表示
// ■標準入力を受け取る
// ■isFinishがtrueならば処理終了。
// ■0か1の値を返却する
// ■0: 〇 1: ×で当たったかどうかの判定
// ■続けるのかどうか判定する

<やったこと>

  1. これらの処理をはじめの状態では、すべてLv2Mainクラスに記述していましたが、これをクラス別に分けました。
  2. 作成した各クラスをメインメソッドで呼び出して実行する。この時に初めの動きと変わらないことを確認しました。

これにより、プログラムのコードがすっきりして(したと自分は思います。。。)、メインメソッドを修正しなくても次の部分の処理が、修正が可能になりました。

1. ■ゲーム開始文言を表示: MarubatsuConsoleの修正
2. ■標準入力を受け取る: MarubatsuUtilsの修正
3. ■0か1の値を返却する: MarubatsuUtilsの修正
4. ■0: 〇 1: ×で当たったかどうかの判定: MarubatsuUtilsの修正
5. ■続けるのかどうか判定する: MarubatsuConsoleの修正

「そのように作ったんだから当たり前だろ?」と思った方、正常でございます。
このような作り方が、クラスを拡張する、アプリケーションを拡張するときに、役立つのです。

今回の「〇×あてゲーム」のような小さなレベルのプログラムはファイル一枚で何も問題はありませんが、会社の業務で使用するような大きなアプリケーションでは、いろんな機能が必要なので、役割分担を行い、機能拡張がしやすい形で、プログラムを組んでいきます。

拡張する準備をする

初めに「必要になること」について記載したのですが、触れていない部分があります。次の部分です。

  • 各クラス(部品)の単体テスト(UnitTest)ケースを用意しておき、修正したら即テスト、部品を取り換えるだけでよいように、作成した資源(プログラムのコード)を結合テスト・総合テストと実施できるような体制を整える

具体的にどのようなことか?これについて、記載したいと思います。

JavaならばJUnit

JavaのテスティングフレームワークといえばJUnitです。
具体的に、どのように使うか記載したいと思います。

役割分担をしたクラスのテストクラス作成

作成した各クラスのテストケースを作成するためのクラスを作成します。
クラス名は、「対象のクラス名 + Test」の形で作成します。

  • MarubatsuConsole: MarubatsuConsoleTest
  • MarubatsuUtils : MarubatsuUtilsTest

作成したコードは次の通りです。ただし、MarubatsuConsoleTestは、標準出力の内容を取得して。。。とちょっと面倒なので、今回は実装しません。

<JUnit実行結果>

package jp.zenryoku.tutorial.level2;

import org.junit.jupiter.api.Test;

import java.util.Scanner;

import static org.hamcrest.core.Is.is;
import static org.junit.jupiter.api.Assertions.*;
import static org.junit.matchers.JUnitMatchers.either;

public class MarubatsuUtilsTest {
    /** テスト対象クラス(全てstaticメソッドなのでインスタンス不要) */
    private static MarubatsuUtils target;

    @Test
    public void testGetScanner() {
        Scanner scan = MarubatsuUtils.getScanner();
        // インスタンスが取得できていることを確認
        assertNotNull(scan);
    }
    @Test
    public void testNextInt() {
        int res = MarubatsuUtils.nextInt(2);
        // インスタンスが取得できていることを確認
        boolean isRondom = res == 0 || res == 1;
        assertTrue(isRondom);
    }

    @Test
    public void testIsAtari() {
        assertTrue(MarubatsuUtils.judgeAtariOrNot(0, 0));
        assertTrue(MarubatsuUtils.judgeAtariOrNot(1, 1));

        assertFalse(MarubatsuUtils.judgeAtariOrNot(0, 1));
        assertFalse(MarubatsuUtils.judgeAtariOrNot(1, 0));
    }
}

でわでわ。。。

<<< 前回 次回 >>>

Java Basic 中級編 ~①アプリケーション設計を考える~

イントロダクション

Javaの基本文法が理解できたところで、実際に動くものを作ってみたいと思うのが人情だと思います。
基本文法だけでも、簡単なプログラムを作ることができます。
例えば、下のようなものです。

単純なゲームループ

1. アプリケーションを作るために

今までに、Javaの基本を学習してきました。大まかに次のようなことを学習しました。

  1. メインメソッドを動かすこと
  2. Stringやintなどのデータ型があり、それぞれに意味があること
  3. 条件分岐、繰り返しなどの文法(if文、switch, while, forなど)
  4. 自分で作ったクラスもデータ型として宣言することが出来て、呼び出す(動かす)事ができること
  5. JavaAPIで提供しているクラス(java.lang.Math, java.util.Scannerなど)を使用できること

これらのことを、このブログでは「Java Basic」と言うカテゴリで作成しています。

お勧めする学習フローも一応あります。Javaの学習をするのにこの順番で学習していくとわかりやすいであろうというものです。
※筆者が紆余曲折した結果、「この順序がおススメ!」というものです。

この基本に対して1段上の領域があります。それは「オブジェクト指向」と呼ばれている考え方なのですが、これの解釈が人によって千差万別なので、この言葉は使わないようにしようと考えております。

そして、開発環境を整えておきましょう。

Eclipseで開発

IntelliJで開発

2. アプリケーションを作り、運用することも考える

オブジェクト指向プログラミングは、世間では色々といいますが、
どちらかといえば、「エクストリーム・プログラミング」のほうが近いように思いますが、自分でよりより良い実装ができれば良いと思います

エクトリーム・プログラミングに関しては上記のリンク先を参照してください。
早い話が、次のようなものです。

ソフトウェア品質 を向上させ、変化する顧客の要求への対応力を高めることを目的としたソフトウェア開発プロセスである。アジャイルソフトウェア開発の一つとして、短い開発サイクルで頻繁に「リリース」することを推奨することで、生産性を向上させ、新しい顧客の要求を採用するためのチェックポイントを導入することを意図している。

これを実現するために、次のようなことが求められます。

  • 各プログラム間(Javaのクラス同士)の依存度を限りなく低くする。
  • プログラムを拡張するのに、元のプログラムをほとんど変更しない。
  • Javaを使えるレベルの知識(技術)がある人なら、誰が見てもわかるようなコードを書く。
  • 各クラス(部品)の単体テスト(UnitTest)ケースを用意しておき、修正したら即テスト、部品を取り換えるだけでよいように、作成した資源(プログラムのコード)を結合テスト・総合テストと実施できるような体制を整える

個人で実現するのは、結構な苦労ですが、一人でアプリのリリースまでやろうと考えるならやっておきたいところです。

3. 基本が大前提

Javaでなくてもそうですが、基本ができないと応用はできません。
基本というのは、上記でいうところの「Java Basic」は、たとえて言うならば、「Javaの基本」という本があったとしたら、その上巻に当たります

「じゃ、実際どの部分が基本なの?」という疑問が出ると思います。
これは、自分の見解ですが、下のように考えています。

つまりは、Javaだけでなくプログラミングの基本には次のような段階があると思うということです。

  • 「Javaの基本:上巻」は小さなレベルのプログラム(アプリケーション)が作れるレベルの基本。
  • 「Javaの基本:下巻」は小さなレベルのプログラムを拡張して、どんどん新しい機能を追加していけるレベルの基本

具体的には、下の通りです。

  • 変数の扱い方
  • クラスの作り方
  • 基本文法
  • javaライブラリの基本的な使い方

    今後は、「Javaの基本」という本の下巻に当たる部分を学習します。

今までの学習方法(古い時代の学習方法だと思います。。。)は、とりあえず何かしらのアプリケーションを組み続けてそこから、自分で上にも書きましたが、次のことを理解していきました。

  • 各プログラム間(Javaのクラス同士)の依存度を限りなく低くする。
  • プログラムを拡張するのに、元のプログラムをほとんど変更しない。
  • Javaを使えるレベルの知識(技術)がある人なら、誰が見てもわかるようなコードを書く。
  • 各クラス(部品)の単体テスト(UnitTest)ケースを用意しておき、修正したら即テスト、部品を取り換えるだけでよいように、作成した資源(プログラムのコード)を結合テスト・総合テストと実施できるような体制を整える

これらのようなことを理解するのには、自分の場合、大体3年くらいかかりました。
あくまでも、基本が理解できたというレベルです。

そこから、このような基本を応用し、実践していくことが必要になりますが、それこそは実際にやってみるしかありません。

基本ができたら、そこから先は自分の作りたいものをどんどん作っていくということが最大の課題になると思います。自分も時間を見つけてやっています。いまだに完成までいかないものがあります。。。

やはり、「モチベーションの維持と、健康の維持」が大きな課題となります。

4. 小さなレベルのプログラムを拡張する

先ほどから、「小さなレベルのプログラムを拡張する」と記載していますが、これは一体どういうことなのか?これについて、記載したいと思います。

基本はプログラムを修正しない

小さなレベルのプログラムを拡張するときに、なるべくプログラムを修正しないようにプログラムを組んでいくというところが、このレベルでの基本になります。
それには、クラスとクラスの関係をうまく作るということがカギになるのですが、具体的なコードを見ていくほうが早いので、サンプルコードを見ていきましょう。

〇×あてゲーム

単純なアプリケーション「〇×あてゲーム」を作成しました。下のようなコードで動きました。
※コードはGithubにあります

/**
 * Javaの基本レベル2:小さなレベルのプログラムを拡張する。
 * 今回は、〇か×か当てるゲーム。
 */
public class Lv2Main {
    /** 週r等フラグ */
    private static boolean isFinish;

    public static void main(String[] arg) {
        isFinish = false;
        // 標準入力を受け取るクラスをインスタンス化
        Scanner scan = new Scanner(System.in);

        while (true) {
            System.out.println("「〇×あてゲーム」 0: 〇 1: ×。");
            // 標準入力を受け取る
            int input = scan.nextInt();
            // isFinishがtrueならば処理終了。
            if (isFinish) {
                System.out.println("プログラムを終了します。");
                break;
            }
            // 0か1の値を返却する
            Random rdm = new Random();
            int res = rdm.nextInt(2);

            // 0: 〇 1: ×で当たったかどうかの判定
            boolean isAtari = res == input;
            String value = input == 0 ? "〇" : "×";
            if (isAtari) {
                System.out.println("あたり:" + value);
            } else {
                System.out.println("はずれ:" + value);
            }
            System.out.println("続けますか? 0: 続ける  1: やめる");
            int next = scan.nextInt();
            if (next == 1) {
                break;
            }
        }
    }
}

次回

このプログラムをなるべく修正しなくても機能拡張できるように修正していきます。
現状のプログラムは、修正しようとしたら必ずメインメソッドを修正する必要があります。これを解消するために試行錯誤します。

でわでわ。。。

次回 >>>

Java SQL はじめて ~オリジナル・データベースを作ろう~

Java SQL

javaでSQL、つまりはDB(データベース)に接続してテーブルを作ったり、データを登録したり。。。

早い話が、オリジナル・データベースを作ろうというわけです。

SQLってなに?

SQLはデータベース(DB)コントロールするためのプログラム言語です。大まかに下のような操作を行います。

  1. テーブル(表)の作成、変更、削除: CREATE,ALTER DROP
  2. データの操作: INSERT, SELECT, UPDDATE, DELETE

これだけです。細かい話をするともっとありますが、大まかにこのくらいです。

そして、実際に利用するときはポピュラーな用途として次のようなものがあります。

  • 顧客データ(ユーザーデータ)
  • 商品データ
  • ブログの記事データ

データベースというアプリケーションを使用して、様々なデータを保存、利用する事ができます。

例えばRPG

たまたま作成している学習用のプログラム、テキストRPGでもデータベースを使用します。
そのように作れば、どこでも使うことになるのですが。。。

RPGゲームを作るのに、アイテム、武器、防具などを、
手書きで。。。
ファイルに保存して。。。
それを読み込んで。。。

などとやっていてはいつまでたってもプログラムが完成しません。
もちろん、メモリをたくさん使用するので、アプリケーションとしても遅いものになります。

DBを使う

このデータベースというのは、保存したデータを関連付けることができるので重宝されています。
俗にいうリレーショナルデータベースというものです。最近はグラフDBなどのような新しいものもあり餡巣が、まずはリレーショナルデータベースの理解が先です。

こちらにDerbyというデータベースを使用したときの記事がありますので、よかったらどうぞ。

DBについて

基本情報の学習をしたときに記載した記事がありますので、こちらを参考にDBに関して記載したいと思います。

上記の記事は、基本情報技術者試験の学習なので、SQLに関しては記載しませんでしたが、この記事では記載します。

H2DataBase

このデータベースを使用します。簡単なのです。以前、Derbyというものを使用しましたが、結構手間なのでこちらを使用することにします。

H2DBをインストール

こちらのリンクが、H2DBのサイトになります。

英語ばかりですが、臆することはありません。赤枠の部分をクリックすればよいのです。

すると「h2-setup-2019-03-13.exe」というようなファイルがダウンロードできるはずですので、これを起動してインストールします。そして、以下の手順に従います。

  1. H2DBを起動する ※H2 Consoleという名前があるのでそれをクリック
  2. 下のような画面があるので、赤枠の部分にDBを作成するフォルダとファイル名を指定する

    ※「D:\Apps\H2\」のフォルダに「database.mv.db」ができます。

そして、ユーザー名などは指定していないので、未入力の状態にして「接続」をクリックします。

すると下のような、画面が見れますので、赤枠をクリックします。

すると下のようなSQLが生成されて、実行する事ができます。

DROP TABLE IF EXISTS TEST;
CREATE TABLE TEST(ID INT PRIMARY KEY, NAME VARCHAR(255));
INSERT INTO TEST VALUES(1, 'Hello');
INSERT INTO TEST VALUES(2, 'World');
SELECT * FROM TEST ORDER BY ID;
UPDATE TEST SET NAME='Hi' WHERE ID=1;
DELETE FROM TEST WHERE ID=2;

作成されたテーブルは、サンプルのテーブルです。とりあえず作成されたことを確認できますのでこれを実行します。

実行して、テーブルが作成されたことを確認したら、「切断」して初めの画面に戻ります。

JDBC接続

次は、Eclipseを起動して、プログラムを作成します。
Eclipseに関しては、設定、セットアップをこちらの記事に記載していますので、参照ください。

まずは、「H2dbManager」クラスを作成します。動画はクラスの作成方法を示したものです。

ポイント

DriverManager.getConnection("jdbc:h2:D:\\Apps\\H2\\database");

上記の「D:~」の部分は、H2DBに接続するときに書いているJDBCのURIになります。

「D:\Apps\H2\」のフォルダに「database.mv.db」ができます。

public class H2dbManager {
    /** このクラスのインスタンス */
    private static H2dbManager instance;
    /** DBコネクション */
    private Connection con;

    /**
     * プライベートコンストラクタ、H2DBのコネクションを取得する。
     * {@link H2dbManager#getInstance()}
     */
    private H2dbManager() {
        try {

            con = DriverManager.getConnection("jdbc:h2:D:\\Apps\\H2\\database");
            Statement stmt = con.createStatement();
            ResultSet result = stmt.executeQuery("select * from TEST;");
            result.next();
            String id = result.getString(1);
            String name = result.getString(2);
            System.out.println("ID: " + id + "Name: " + name);
        } catch (SQLException e) {
            e.printStackTrace();
            System.exit(-1);
        }
    }

    /**
     * このクラスのインスタンスを取得する。
     * @return H2dbManagerインスタンス
     */
    public static H2dbManager getInstance() {
        if (instance == null) {
            instance = new H2dbManager();
        }
        return instance;
    }
}

上のコードが書けたら、実行します。実行の仕方は、以下のようなメインメソッドを作成して実行すればOKです。

public static void main(String[] args) {
    H2dbManger main = H2dbManger.getInstance();
    main.executeQuery("select * from test;");
}

追伸、下のように使用するSQLで呼び出すメソッドが違います。

// SELECT文
main.executeQuery("select * from test;");

// INSERTやUPDATE文
main.executeUpdate("INSERT INTO MyTable(ID, NAME, VALUE) VALUES(1, 'test太郎', 12);");

// CREATE TABLEなどのCRUD以外のSQL ※CRUDのSQLも実行可能
main.execute(sql);

これでJavaでのDB接続は完了です。あとは、SQLを実行してデータの登録やらテーブルの作成やらやってみるのが面白いと思います。

でわでわ。。。

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

イントロダクション

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

クラスの役割分担をする

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

CommandIF(インタフェース)を使用したポリモーフィズムの実行動画があります。

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

  • メインメソッドを持つクラス
  • じゃんけんゲームを起動するクラス
  • じゃんけんの入力や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)します
<作成するインターフェース>
```java
public interface ExeInterface {
// 抽象メソッド(implementsしたクラスに実装を強制する)
public execute(Scanner scan);
}
```

<FirstClsにimeplementsする>
```java
public class FirstCls implements ExeInterface {
// 中身は省略
}
```

```java
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を作成したとします。
メインメソッドも下のように修正します。

```java
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
```

<プロパティファイルを読み取る例>
```java
/** コンストラクタ */
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);
}
}
```

<プロパティファイルのキーから値を取得する例>
```java
/**
* プロパティファイルから値が取得できた、値を返し
* 取得できない時はから文字("")を返す
* @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);

の部分です、リフレクションの実装になります。クラスの完全名からクラスオブジェクトを取得、インスタンスの生成。というような処理を行います。
```java
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/)