1章:⑩追加実装を行う〜入力チェックと表示の変更〜

目次はこちら

1-7 追加実装を行う〜入力チェックと表示の変更〜

今までに作成したきた、処理フローには仕様が不足しています。

それは「入力チェックがない!」というところです。

じゃんけんゲームでもし、間違ってグーチョキバーに対応する0~2以外の値が入力されたら

多分NullPointerExceptionで落ちます。

勝敗判定マップからNULLを取得するためです。

なので、入力チェックを追加します。

そして、ユーザーとCPUの手を表示していません。

じゃんけんゲームなのに相手の手が見れないと結果だけ見てもつまらないと思います。

本パートでは、発見した不足(バグ)に対応するためのテクニックを学びます。

そして、以下のことを行います。

本パートで行うこと

  1. 入力チェック処理の追加
  2. ユーザーとCPUの手を表示する処理の追加
  3. メインメソッドの起動確認
  4. 学習してきたことのまとめ

1.入力チェック処理の追加

現状、筆者が作成したメインメソッドの処理内容は下のようなものです。

/**
 * メインメソッド
 * @param args プログラム引数
 */
public static void main(String[] args) {
  // 0.じゃんけんゲーム起動
  FirstJankenMain main = new FirstJankenMain();
  // 1.勝敗判定MAP作成
  main.createJudgeMap();
  Random random = new Random();

  // 無限ループ
  while(true) {
    // 2.「じゃんけん」or「あいこで」のメッセージ表示
    main.printJankenAiko(isJanken);
    // 3.ユーザーの入力(待ち)
    String input = main.acceptInput();
    // CPUの手を取得する(JavaSEのAPIを使用するのでテストしない)
    String cpuTe = String.valueOf(random.nextInt(2));
    // 4.「ポン!」or「しょ!」を表示
    main.printPonOrSho(true);
    // 5.勝敗判定
    int judge = main.judgeWinLose(input, cpuTe);
    // 6.勝敗判定の表示
    if (main.printJudge(judge)) {
      break;
    }
  }
  // 7.じゃんけんゲーム終了
}

この実装に、入力チェックを追加するには、単純にメソッドを追加してやればよいです。

処理フロー(箇条書き)には、3と4の間に「ユーザーの入力チェックを行う」が入ります。

今までのやり方と同じように空実装でメソッドを追加します。

/**
 * じゃんけんの手として適当な値であるか判定する。
 *
 * @param input ユーザー入力
 * @return true; じゃんけんの手として適当な値 / false: じゃんけんの手として不適当な値
 */
private boolean inputCheck(String input) {
}

そして、メインメソッドに上のメソッドを追加する。※実装サンプルは割愛します。

テストケースも同様に追加します。筆者が作成したテストケースは以下のようなものです。

確認する事は以下のようなものです。

  1. 0~2までの値が入力された(引数に渡された)場合、Trueを返す
  2. 上記以外が渡された場合はFalseを返す

実装サンプルは下のものです。※今回は「修飾子」を「private」に変更しているので

下のようなメソッドを使用してテストを行います。

※privateメソッドのテスト方法に関しては「1-4 実装とテスト〜JUnitを使用して、テストケースから作成〜.md」を参照ください。

private Method getPrivateMethod(Class clazz, String methodName, Class<?> ... paramType) {
    // テスト対象クラスを返却する
    Method testMethod = null;
    try {
        testMethod = clazz.getDeclaredMethod(methodName, paramType);
    } catch (NoSuchMethodException e) {
        e.printStackTrace();
    } catch (SecurityException e) {
        e.printStackTrace();
    }
    return testMethod;
}

@Test
public void testInputCheck() {
    // テストするメソッドを取得する
    Method test = getPrivateMethod(target.getClass(), "inputCheck", String.class);
    // テストを実行する
    boolean res = false;
    try {
        // プライベートメソッドのアクセスを可能にする(テストの時だけ使用するようにする)
        test.setAccessible(true);
        // プレーヤーの勝ち(文字列からINT型に変換
        Assert.assertTrue((boolean) test.invoke(target, GU));
        Assert.assertTrue((boolean) test.invoke(target, CHOKI));
        Assert.assertTrue((boolean) test.invoke(target, PA));
        // 想定外の値1
        Assert.assertFalse((boolean) test.invoke(target, "3"));
        // 想定外の値2
        Assert.assertFalse((boolean) test.invoke(target, "-1"));
    } catch (IllegalAccessException e) {
        e.printStackTrace();
        Assert.fail("アクセスの仕方に問題があります。");
    } catch (IllegalArgumentException e) {
        e.printStackTrace();
        Assert.fail("引数に問題があります。");
    } catch (InvocationTargetException e) {
        e.printStackTrace();
        Assert.fail("メソッドの起動時に問題が発生しました。");
    }
}

くどいようですが、処理の内容を記述します。

前提として、\@BeforeClass(BeforeAll)アノテーションにてテスト対象クラスをインスタンス化しています。

  1. テスト対象クラスのメソッドクラスを取得します。
  2. メソッドのアクセス修飾子が「private」なので、これをアクセスできるように変更します。
  3. メソッドクラスに実行する引数(グー、チョキ、パー)を渡して、メソッドを起動します。
  4. それぞれの処理が想定内の値ならば「true」、想定外ならば「false」を取得することを確認します。

プライベートメソッドのテスト方法

改めて、プライベートメソッドの処理内容について記述します、以下の通りです。リフレクションを使用しています。

この実装は、初心者でなくても使用する人が少ない実装ですので、「こんな実装もあるんだな」と思ってもらえればよいです。

リフレクションとはjava.lang.refrectパッケージを使用した実装のことです。

  1. getPrivateMethod()でテストするメソッドを取得
  2. アクセス制限を解除
  3. メソッドの実行
  4. 0が渡された場合Trueを返すことを確認
  5. 1が渡された場合Trueを返すことを確認
  6. 2が渡された場合Trueを返すことを確認
  7. 3が渡された場合Falseを返すことを確認
  8. -1が渡された場合Falseを返すことを確認

早い話が、プライベートメソッドを取得してアクセス制限を解除、そしてテストの実行ということです。

リフレクションは、対象のクラス(オブジェクト)からメソッドやフィールドを取得することができます。

そして、メソッドのみを取り出し、実行させることができるのでクラスを「new」しなくてもメソッドの実行ができます。

この技術は、Springframeworkで使用しているDIに使用されていると思われます。

※筆者の予想になります。

java.lang.refrectが出てきたので少しびっくりしたと思いますが、これを知っておくと

Springframeworkなどのテスト時に役に立ちます。リフレクションのオンパレードになりますので。。。

具体的には、Mockitなどを使用していることが多いです。

2.CPUの手を表示する処理の追加

これも、入力チェックと同様です。メソッドを追加、テストケースを追加

取得したCPUの手とユーザーの入力をコンソールに表示するだけです。
これは、表示用のメソッドを一つ作成してメインメソッドの中で呼び出してやれば問題ないですね。

サンプルとしてメソッドの定義だけ記述しておきます。

public void printTe(String userTe, String cpuTe) {
}

3.メインメソッドの起動確認

これは、単純にアプリケーションを起動して動きを確認します。

今まで作成したテストケースがあるので、以下のような作業で事足りると思います。

  • 不足する部分はメソッドの追加
  • 少々のメインメソッドの修正

まとめると、今までやってきたことが、不足分の追加・修正のテクニックになります。

たくさんあるので言葉にしずらいですが、「メソッドの切りだし」と「テストケースの作成」が

行えるようになっていれば、どのように追加・修正を行ったらよいか見えてくると思います。

兎にも角にも、基本の理解が前提になるので、みっちり基本を学習することをお勧めします。

前のパートでも書きましたが、練習問題集を作成したのでこれのLV.20くらいまでクリアできれば、基本的なことは問題ないと思います。

4.学習してきたことのまとめ

  1. Javaの基本
  2. 設計(フローチャートの作成)
  3. 詳細設計(フローチャートの処理部分の設計)
    • 処理部分をメソッドとして切り出す
    • メインメソッドでフローチャート(処理)の流れを作る
    • 実装方法を理解し実装のイメージを作る
  4. 設計(詳細設計)した処理の確認方法を明確にする
    ※JUnitで確認処理を実装
  5. テストケースを実行して成功させる(緑色になるようにする)

本パートでは以上になります。

次は、オブジェクト指向プログラミングの考え方を使って

作成した「じゃんけんゲーム」を再度作成します。「継承」の話もします。

オブジェクト指向プログラミングの考え方を使った場合と

そうでない場合の違いが明確になると思います。