Java はじめて23 〜テスト駆動型開発を行う〜

イントロダクション

テスト駆動開発と聞いて、「すげーことやんのか?」と思った方ごめんなさい。
シンプルに実装の順番が違うだけです。
<サンプル>

今までの開発の場合

  1. 設計: プログラムの設計(アプリケーションの設計)
  2. 実装: 設計に従い、プログラムを実装(アプリケーションのの実装)
  3. ユニット・テストの作成: 作成したプログラムがちゃんと動くかチェックするためのテストケース(テスト仕様)を作成
  4. ユニット・テスト実施(単体テスト)
  5. 結合テスト: 今まで作成したプログラムを全て繋げて、アプリケーションをして動かしてテスト仕様通りに動くか確認
     ※画面の操作を行うようなテストは、結合テストです。

テスト駆動開発の場合

  1. 設計: プログラムの設計(アプリケーションの設計)、テスト仕様の作成
    テスト仕様も作成するので、詳細な設計が必要になります。なので、「このフラグはどこのクラスでもつの?」などの設計不足が減ります。

  2. ユニット・テスト: これから作成するプログラムがテスト仕様通りに動くことを確認するプログラム、テストケースを作成
    プログラムレベルでのテストケースを作成するので、「どのように動けばよいか?」が設計レベルで明確になります。

  3. 実装: 設計に従い、プログラムを実装(アプリケーションのの実装)、ただしテストケースが全て通るように作成する
    テストケースが通るように作成するので、「ゴール」が見える状態で実装することができる。

  4. 結合テスト: 今まで作成したプログラムを全て繋げて、アプリケーションをして動かしてテスト仕様通りに動くか確認

上記のように作業工程が1つ少ない形での作業になります。仕様(このように動く、という取り決め)が先行して作成されるので出来上がるアプリケーションも、想定通りに作成することができ、作業工程が1つ減るのでスピードも上がります。
そして、実装とユニットテストが癒着しているので、実装で起動確認を行うときにユニットテストができます。

テストケースの作成

初めに、JUnitのライブラリを参照する様に設定します。

やり方は、以下を参照下さい。

開発環境構築~Windows版Eclipseの設定~

開発環境のセットアップを初めから記載しています。使用しているのはEclipseです。

今回は、JUnitを使用して以下のことを実行します。

  1. テスト仕様の作成
  2. テストケース(コード)の作成
  3. 本番のクラスを作成する

テスト駆動型開発

世間ではいろいろなことが言われていますが、シンプルに「テスト仕様から作成する方法」という意味で記載しています。
シンプルに「テスト仕様から作成する」ということはどいうことかに関して、ちょいと記載します。
早い話が、「このクラスはどう動けば良いの?」から考える実装方法だと思っています。「テスト駆動型開発」という言葉を作った人間ではないので、この様な書き方になります。

ちなみに、JUnitが使用できるかどうかに関しては下の様に「JUnit」がワークスペース上に表示されていれば大丈夫です。下の画像には「JUnit5」があります。

例外として、存在するけど動かないということもありますが、それはエラーが出力され、コンソールに「XXXException」と表示されるのでそれをインターネットで調べてみると解決します。
ちなみに、自分の場合は以下の様にアノテーションをつけてやると動きました。Springなどのフレームワークを使用している場合は、それぞれのテストさ癖方法があるのでそちらを参照されたし。。。JUnit5を使用している場合です

@@RunWith(JUnit5.class)
public class MyTest {
テストケース
}

テスト仕様を考える

前回作成した、仕様と設計より「口座管理クラス」を作成することにしました。この口座管理クラスはファイルを使用してデータを保存したり、更新したりします。

本番で使用するクラスとテストクラスを分けるために以下の様に完全クラス名は同じだけどルートフォルダが違う形で作成します。
今回作成するクラスは、KozaManagerクラスです。

そして、テストクラスは下の様に「test」フォルダ以下に作成します。

テスト仕様

テスト仕様というと堅苦しい感じがしますが、早い話が「どう動けば良いか?」を決めるところです。ちょっと慎重にいきます。
しかし、今回はファイルに出力するだけなので深く考えません。

ちなみに、深く考えると下の様になります。

テスト仕様

  1. 操作するファイルが存在しなければ、作成し、存在すればそのまま参照する
  2. 同様にファイルへ(口座の)ユーザー情報をファイル(CSV形式)への出力、保存ができること
  3. 同様にファイルの読み込みができること
  4. 読み込んだファイルのデータをユーザー情報クラスで取得できること(同様にファイルへ出力できること)

CSVファイルを作成

CSVファイルはカンマ区切りのテキストファイルです。

項目名1, 項目名2 , ....
1行目データ1, 1行目データ2, ...
2行目データ1, 2行目データ2, ...

この様な形で、データを保存します。今回は、口座情報なのと最小限度の情報を登録するので、ユーザーとパスワード、預金額をデータとして保存します。

早速テストが動くか確認します。
```java
public class KozaManagerTest {
/** テスト対象クラス */
private KozaManager target;
/**
* テストの初期化
* 各テスト実行前に起動する
*/
// @Before
// public void initClass() {
// target = new KozaManager();
// }
/**
* isFileメソッドのテストケース
*/
@Test
public void testIsFile() {
System.out.println("Hello JUnit");
}
}
```
ちなみに、コンストラクタを動かしたくないので今はコメントアウトしています。まずは、テストが動くか確認します。

テストを実行するときは下の様に表示されます、

<JUnitでデバックをしてみた>

他にも、こんなことをやってみました。

## JUnitでテスト作成
だいぶ説明がザツだったので、改めます。。。
### テスト作成準備
まずは、上記のようにテストクラスとテスト対象クラスを準備します。
テスト対象クラスは、まずメソッドの側だけ作成しておきます。

#### Step1: プログラムコードを書いてしまう。
テストケースを作るという言い方もあります。テストプログラムを書いてしまいます。

**<KozaManagerTestクラス>**
```java
public class KozaManagerTest {
/** テスト対象クラス */
private KozaManager target;

/**
* テストの初期化
* 各テスト実行前に起動する
*/
@Before
public void initTest() {
target = new KozaManager();
}

/**
* コンストラクタが起動したかどうかを確認する
* テストケース
*/
@Test
public void testIsInstance() {
assertNotNull(target);
}

/**
* ファイルの存在チェック処理の確認
*/
@Test
public void testIsFile() {
// コンストラクタでファイルが作成されることに注意
assertTrue(target.isFile());
}
}
```

はじめの段階では、**KozaManager**クラスのメソッドはエラーが出るので、中身のないメソッドだけ作ってしまいましょう。
具体的には、下のような形です。
```java
public class KozaManager {
/** 空(から)のコンストラクタ */
public KozaManager() {
}
/** 空のメソッド */
public boolean isFile() {
}
}
```
そして、テストプログラムを動かすことを考えると「コンストラクタで何をしよう?」「isFile()でファイルの有無をどうやって確認する?」と疑問が出ます。筆者が考えたのは、コンストラクタで、ファイルの読み込み準備をしてしまい、isFileでは、ファイルの有無を返すだけにしようと考えました。

<補足>
・ファイルの場所は、「resources/koza.csv」とする。

**<KozaManagerクラス>**
```java
public class KozaManager {
/** ファイルへの書き出しクラス */
private BufferedWriter write;
/** ファイルの読み込みクラス */
private BufferedReader read;
/** 取得(作成)するファイルクラス */
private File file;
/** ファイルのパス */
private static final String FILE_PATH = "resources/koza.csv";

/** コンストラクタ */
public KozaManager() {
// 操作するファイルを指定する
file = new File(FILE_PATH);
try {
write = new BufferedWriter(new FileWriter(file, true));
if (file.exists()) {
read = new BufferedReader(new FileReader(file));
}
} catch (IOException ie) {
ie.printStackTrace();
System.out.println("ファイルオープンに失敗しました。" + ie.getMessage());
System.exit(-1);
}
}

/** デストラクタ */
@Override
protected void finalize() throws Throwable {
// フィールド変数の後始末
file = null;
read.close();
write = null;
read = null;
}

/** 作成するファイルが存在するかチェック */
public boolean isFile() {
return file.exists();
}
}
```
処理の順序としては、次のように実装します。
1. コンストラクタで、指定のファイルを読み込み、存在しないときは作成する。
2. Dataクラスに、顧客情報を保存しておき、それをCSVに出力するようにする。

#### Step2: クラスのテスト(起動確認)を行う
テストプログラムの実行を行うだけです。これで、プログラムの実行結果が全部緑になればOKというわけです。
これがOKになったら、次の実装、ファイルhに書き込むとか、データクラスの取得とかそれらを考えて実装、テストの追加を行っていきます。

ここら辺は、慣れが必要なので、やるしかありません。

今日はこの辺で失礼します。でわでわ。。。

<<< 前回 次回 >>>

<Java関連の動画リスト>

<JUnit関連の動画リスト>

でわでわ。。。



Java はじめて22 〜オブジェクト指向的分析、アプリ拡張の実装とテスト〜

イントロダクション

今回は、作成した仕様、設計した機能に関して実装していこうと思います。前回作成した仕様、機能に関しては以下にまとめておきます。

仕様と機能

仕様
1. 口座開設している人の情報を保存する
2. 口座を持っている人を特定する
3. 口座の預金額と口座を持っている人を一意にする

そして、上の仕様をいっぺんに実装するのは自分の頭では追いつかないので諦めて、1つずつ考えていこうと思います。
まずは、仕様1「口座を開設している人の情報を保存する」に関して、ますは「口座を開設する」機能が必要です。というかこれがないとこれ以降の機能が実装できません。
なぜか?口座が作成されないと口座を開設している人が追加できない、つまり口座を持っている人が0人のまま。。。
ということになるからです。

機能
1. 口座の情報をファイルに保存する
2. 口座の情報は「名前」と「パスワード」とする
3. ファイルには、カンマ区切りの「CSV」形式でデータを登録する

ファイル入出力の実装

実装イメージ動画
<ファイルにデータを読み込むサンプル>

やっと実装にたどり着きました。ここまで来た方おめでとうございます。
たまたま、このページに来た方、ブログなのでいつでもこれ以前の記事を参照できます。下のリンクで一覧が見れます。よかったらチャレンジしてみてください。
Java はじめて 学習フロー

java.io.Fileクラス

このクラスを使用してファイルの操作を行います。ここで注意して欲しいのは「Fileクラス」はファイル、ディレクトリ(フォルダ)を表現するクラスであり、「入出力」は別のクラスを使用する必要があるというところです。

java.io.BufferedWriterクラス

上のFileクラスと併用してこのクラスでファイルへの書き出しを行います。四の五の言っても意味がないので、コードから見ていきます。まずは、上の2クラスの作成(生成)を行います。口座を操作、管理するクラスは「KozaManager」と名付けました。

package jp.zenryoku.apps.atm.manage;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;

/**
 * 口座の管理(登録、更新、削除)を行うクラス
 * @author takunoji
 *
 * 2019/09/21
 */
public class KozaManager {
    /** ファイルへの書き出しクラス */
    private BufferedWriter write;
    /** ファイルの読み込みクラス */
    private BufferedReader read;

    /** コンストラクタ */
    public KozaManager() {
        // 操作するファイルを指定する
        File file = new File("resources/koza.csv");
        try {
            write = new BufferedWriter(new FileWriter(file));
            if (file.exists()) {
                read = new BufferedReader(new FileReader(file));
            }
        } catch (IOException ie) {
            ie.printStackTrace();
            System.out.println("ファイルオープンに失敗しました。" + ie.getMessage());
            System.exit(-1);
        }
    }
}

コンストラクタで、ファイルの書き出し、読み込みクラスを作成(生成)しています。注意としては、ファイルの読み込み時にファイルが存在していない可能性があるので「ファイルが存在しているならばif (file.exists()) {...}」リーダー(BufferedReader)を作成しています。
そして、何かしらの例外(ファイル入出力関連の例外)があった場合に備えtry { ... } catch(IOException ie) { ... }で例外処理を作成しています。

テスト駆動開発

私事ではありますが、「テスト」に対しての認識を共有したく思い「テスト駆動開発」でやっていきたいと思っています。
このテスト駆動型開発は「仕様を明確にする」のが至上命令で、「どーゆー風に動けば良いか?」を一番に考えます。。。なんか当たり前な話に聞こえますが、「当たり前」はできていなくてはならない(実際難しい。。。)ことです。まぁ頑張りましょう(笑)
というわけで、テストケースから作成していきます。詳細に関してはこちらを参照してくださいwwww。

余談

テスト駆動型開発がオススメな理由、というか自分の考えですが、テストケースがちゃんとできていれば「テストが通れば、実装内容はほぼ自由」で良いことになります。そして、リファクタリングをするにも「テストが通ればデグレートの心配はない」ということになります。しかもテストは自動で実行できるツール(Mavenなど)があるので、テスト処理を流して帰宅するのもおっけーです(作業時間に余裕が必要ですが。。。)

テストケースの作成

今回のテストはファイルの読み込みと書き出しができることを確認するためのケースを作成します。そして、このテストケースを通る(エラーが出ない)のであれば実装は完了になります。
なので考えなくてはいけないのがいかになります。

テスト仕様

  1. 操作するファイルが存在しなければ、作成し、存在すればそのまま参照する
  2. 同様にファイルへ(口座の)ユーザー情報をファイル(CSV形式)への出力、保存ができること
  3. 同様にファイルの読み込みができること
  4. 読み込んだファイルのデータをユーザー情報クラスで取得できること(同様にファイルへ出力できること)

上の4項目を満たすようなクラスができれば、現状はおっけーです。今回は、ここまでにします。

次は、テスト駆動開発を進めていきます。しかし、「テスト駆動開発」ってなによ?という疑問を解決する必要があると思います。

テスト駆動開発とは?

「テスト駆動」というのはTest Firstを日本語に解釈したら、そうなったのであろうと思っていますが、まぁ言葉は解釈によって意味が変わるので深く考えません。
つまりは、「テストから始めましょう」ということです。そしてテストから始めるのは次の準備が必要です。

  1. 仕様を明確にする。でないと「どう動けば正しいかわからない」からです。
  2. テスト仕様も作成し、「どのようなテストをすればよいか?」を解決する。
     ※ここで、開発しはじめてから「やっぱできませんでした!」というのがなくなる

メリット

  • 先に仕様を明確にする、つまり設計をちゃんとやるので実装するときに「この部分が不明なんですが。。。」と設計者ともめることもなくなります。
  • 開発時の工程「実装とテスト」を同時にできるので作業工程が一つ減る

でわでわ。。。

<<< 前回 次回 >>>

<Java関連の動画リスト>



Java はじめて21 〜オブジェクト指向的分析、アプリの拡張をする〜

イントロダクション

今回も、コーダー銀行(コンソールアプリ)の拡張をします。つまりは、機能追加をします。

「オブジェクト指向」という言葉に関して

一時「staticおじさん」という言葉がはやりました、ちょっとわかりずらい言葉であるため、いろんな人が色々な解釈をするので「カオス化した言葉」という認識を持っております。「関数型」に関しても同様なことが言えると思います。※MonoBook参照

混乱を防ぐため「オブジェクト指向」という言葉の定義に関しては下のリンクを参照下さい。

Java オブジェクト指向基礎 ~オブジェクト指向コンセプト~

今までに、以下の内容を実施(記載)してきました。

  1. 作成したアプリの修正
  2. チェック処理クラスの追加

現状の処理としては、コーダー銀行への「入金」と「引き出し」ができるだけであまり役に立たなそうです。
なので、今度はもっと銀行らしく口座の開設機能を作る方向へ向けて機能拡張を考えて見たいと思います。

オブジェクト指向的分析

機能拡張をするときには「どこを拡張するか?」つまり「コードのどの部分をどのように修正するか?」という疑問に答える必要があります。
そこで、今までの作成したものを、確認がてらに見直して「修正ポイント」を探し、拡張するための設計を行いたいと思います。

まずは仕様

「どのようなことを実現するか?」を初めに考える必要があります。そして、今回の場合は「コーダー銀行の口座開設」が実現したいことなので、口座の開設のために必要なことを考えます。

  1. 口座開設している人の情報を保存する仕組みが必要
  2. 口座を持っている人を特定する仕組みが必要
  3. 口座の預金額と口座を持っている人を一意にする仕組みが必要

大まかにこんな感じだと思います。あまり細かいと複雑になりクラス設計がややこしくなるので、今回は学習目的なのでこの程度の仕様にしておきます。

現状の構成を把握

今動いているアプリは下のような感じで動きます。

クラスの構成としては以下の3つがあり、それぞれ「メイン処理」と「預金額管理」、「チェック処理」を担当するクラスになっています。※実際のソースはGitにアップしています。ちなみに、パッケージは下のようになっています。
「jp/zenryoku/apps/atm」

実装ポイント

口座の開設処理を行う前に、現状では「入金」と「引き出し」の機能しかありません。この場合だとコーダー銀行の口座は一人だけで誰でも引き出しができてしまうので安全ではありません(学習ようなので。。。)。1つずつ問題を解消していきますが、まずは口座を開設する機能が必要です。
ユーザー(口座保持者)と口座を結びつける処理などは、後で考えます。

筆者は、1度に複数のことを考えることができません。。。

口座開設処理(余談)

通常の業務アプリ(〜銀行さんとかで使用しているもの)は、ものすごい人数が利用できるように「データベース(DB)」を使用しています。しかし、DBを使用する前に、「ファイル操作」に関して理解する必要があるのでそのように実装いたします。

<ファイルにデータを保存するサンプル>

ファイルに口座情報を保存する

今回作成する「コーダー銀行アプリ」は学習ようなので本物のように個人情報をてんこ盛りにしたような実装はしません。単純に作成するため必要最低限の項目のみを使用します。それは以下に示します。

  1. 名前(ヘボン式のローマ字)
  2. パスワード

そして、今回実装する処理としては。。。

ファイルにデータを登録する処理

この機能を実装するのには、今までのやり方からすると「ファイルにデータを登録する役割」のあるクラスを作成し、そのクラスに処理を実装する、ということになります。

これもオブジェクト指向の基本になります。くどいようですが、「役割分担をちゃんとやりましょう」ということです。

設計を行う

ここまできたら、察しの良い人は「ファイル操作クラスを作るんでしょ?」と思うかもしれません。その通りでございます。

そして、実装する内容は以下のようになります。

  1. ファイル操作クラスを作成(実装)する
  2. ファイル操作クラスを使用する部分を追加(修正)実装する

補足

自分で作成したものは「自分で好きなように改造できる」ので、想像を膨らませて「面白いもの」を作ってみるのも良い学習になります。

まとめ

上記の内容をまとめると以下のようになりました。

仕様
1. 口座開設している人の情報を保存する
2. 口座を持っている人を特定する
3. 口座の預金額と口座を持っている人を一意にする
機能
1. 口座の情報をファイルに保存する
2. 口座の情報は「名前」と「パスワード」とする
3. ファイルには、カンマ区切りの「CSV」形式でデータを登録する

次回は、ここで作成した仕様に基づき実際にコードを作成(実装)してみようと思います。

でわでわ。。。

<<< 前回 次回 >>>

<Java関連の動画リスト>

Java はじめて20 〜チェック処理クラスを作る〜

イントロダクション

前回作成したプログラムを改修します。大まかに下の動画のような動きをします。

このプログラムは、githubにアップロードしてあるので、参考にどうぞ。付随するプログラムとしては下のものです。

  • MainBank: 上のリンクと同じものです。メイン処理を行います。
  • Calcuration: 計算処理、貯金金額の管理をおないます。
  • InputChecker: 入力チェックを行います。

上のリンクにあるプログラムは作成済みのものです。

チェック処理クラスの作成

今回も、下のような作成したクラスをカスタムして、入力チェック処理を作成します。
ちなみに前回は、コンストラクタを修正(カスタム)してコーダー銀行ATM(仮装)のアプリを作成しました。

実装内容確認

今回は、コーダー銀行の入力チェック処理を作成してみようと思います。
なんども、同じようなことを記載しますが、オブジェクト指向プログラミングでは「役割分担」が大切です。大雑把に以下のような形(イメージ)で実装します。

  1. 「〜担当のクラス」という役割を割り当てやる
  2. メイン処理で呼び出してやる。

コードで書くと下のような感じです。

A.処理のはじめ「いらっしゃいませ」という挨拶部分。
System.out.println("コーダー銀行へようこそ、入金しますか?引き出しますか?");
System.out.println("あなたの、預金額は ¥" + cal.getYokingaku() + "-です。");
System.out.println("入金の時は「in」、引き出しの時は「out」を、終了する時は「bye」を入力してください。");
B.入力を行う部分
Scanner input = new Scanner(System.in);
while(true) {
    String inStr = input.nextLine();
    if ("in".equals(inStr)) {
        System.out.println("入金処理を行います。");
        cal.nyukin(input, true);
    } else if ("out".equals(inStr)) {
        System.out.println("引出し処理を行います。");
        cal.nyukin(input, false);
    } else if ("bye".equals(inStr)) {
        break;
    } else {
        System.out.println("「in」か「out」を入力してください。");
    }
}
処理の終わり
System.out.println("ATM処理を終了します。ご利用ありがとうございました。");

こんな感じ(イメージ)です。
「なんだ、処理を区分けして説明しただけぢゃねぇか。。。」と思った方、その通りでございます。

処理を区分け」することがはじめの一歩目になります。
改めて、仕様を見て見ます。

  1. アプリを起動してコンソールから「引き出す」「入金」を選択する
  2. それぞれの処理に対して「預金額」から足し算、引き算を行いその結果を表示する

このアプリを作成するために作った「部品」は以下の通りです。

  1. メインメソッドを持ち、入力の受付も行うクラス
  2. 入金、引き出しを行う計算処理クラス

早い話が、以下のようなクラスを作成するということです。

  1. メインの処理を作成する(実装する)クラス
  2. 預金額を管理して、預金額の計算を行うクラス

入力チェック処理

今回のミッションは「入力チェック処理」を作成することです。
なので、「入力処理」を担当するクラスの処理内容を確認します。
上記より、1のメイン処理を作成するクラスがそのクラスになります。

入力処理

ズバリ、Scannerクラスを使用しているところです。
つまり、String inStr = input.nextLine();の部分が入力を受け取っている処理です。
このScannerクラスは、java.utilパッケージにあります。

とにかく入力された値をStringクラスで受け取ります。
そして、入力された値が妥当かチェックしようというわけです。

チェック処理

チェック処理は、以下のようなものを作成しようと思います。

入力チェック1:入金、引き出し、終了の選択をさせる
「in」「out」「bye」の入力以外はエラーにする

そして、入力チェック処理(クラス)を作成したら、ソースの整理をします。

入力チェック処理は、今回の目玉なので処理内容を記載しません。つまりコードは書きません。その代わり、チェック処理を作成した後の整理(リファクタリング)部分について記載します。現在の入力部分は以下のようになっています。※他にもありますが、今回はこの部分を対象とします。

while(true) {
    String inStr = input.nextLine();
    if ("in".equals(inStr)) {
        System.out.println("入金処理を行います。");
        cal.nyukin(input, true);
    } else if ("out".equals(inStr)) {
        System.out.println("引出し処理を行います。");
        cal.nyukin(input, false);
    } else if ("bye".equals(inStr)) {
        break;
    } else {
        System.out.println("「in」か「out」を入力してください。");
    }
}

無限ループする処理の中です。しかし入力チェックと無限ループは関係ありません。(オブジェクト間の依存関係がないということです)ただし、コード上ではループの中で入力チェックを行うので関連性があります。
ちょっとややこし言い方をしましたが、これは以下のようなループ部分
while(true) { ... }
と入力を受ける処理String inStr = input.nextLine();をはっきりと分けて考えるために記載しました。つまりは以下のようにチェック処理を独立させたいということです。

入力チェック処理を担当するクラスを作成し、そのクラスが入力チェックを行うようにする

こうすることで何が変わるか?ということなのですが、これはチェック処理の内容のみを変更したいときにすぐに追加・変更が可能なようにするということです。

チェック処理を独立させる(今回の目玉)

ズバリ、チェッククラスを作成します。名前は「InputChecker」にします。MainBankのループ部分の処理は、以下のような実装にします。

単純に、次の処理を追加しただけです。

  1. 入力チェックを行い、エラーがあればメッセージを返す処理を追加

    String errorMessage = InputChecker.validNyukinHikidashi(inStr);
  2. メッセージ(errorMessage)が、Nullかどうかの判定を追加

最終的に下のようなコードになりました。

while(true) {
    String inStr = input.nextLine();
    String errorMessage = InputChecker.validNyukinHikidashi(inStr);
    // この部分を追加しました。↓
    if (errorMessage != null) {
        System.out.println(errorMessage);
        continue;
    }
    // 追加した部分終了行↑
    if ("in".equals(inStr)) {
        System.out.println("入金処理を行います。");
        cal.nyukin(input, true);
    } else if ("out".equals(inStr)) {
        System.out.println("引出し処理を行います。");
        cal.nyukin(input, false);
    } else if ("bye".equals(inStr)) {
        break;
    }
}

これで、処理中にエラーがあった場合、は上のコードの一部のみを実行するだけで良いので、無駄な処理を行わなくて済みます。つまりは下のコードのみを実行するだけになります。

    String inStr = input.nextLine();
    String errorMessage = InputChecker.validNyukinHikidashi(inStr);

    if (errorMessage != null) {
        System.out.println(errorMessage);
        continue;
    }

もう一つ、入力チェッククラスを新たに作成しているので、チェック処理を追加するときは、InputCheckerクラスの修正をするだけでよくなります。
※どのような実装にするかは、プログラマ側で決めるところです。こういうところが面白い☆

でわでわ。。。

<<< 前回 次回 >>>

<Java関連の動画リスト>



Java はじめて19 〜コンストラクタの役割を見る〜

イントロダクション

前回作成したプログラム「コーダー銀行アプリ」を深掘りして、改造することを考えてみます。
この時に重要になるのはクラスの構成を理解しておくことです。

クラスには、以下の要素があります。※各項目に関してはこちらで解説しています。

  • コンストラクタ
  • メソッド
  • フィールド変数

なので、これらを理解すれば、クラスは扱えるということです。そんなに難しくないでしょ?(笑)

プログラムを見る

<作成したプログラム>

コンストラクタの役割

コンストラクタの役割は、クラスを初期化するというところです。具体的には、クラスで持っているフィールド変数を初期化するところがメインになりますが、他にも必要であれば、そのような実装を行います。
まずは、基本的なフィールド変数の初期化を考えてみます。

デフォルトコンストラクタ
処理が何もないコンストラクタのこと、これは、暗黙的に実装されている。
「暗黙的に」というのは、「コードに書いてなくても」という意味

フィールド変数
これは、クラスのインスタンスが存在している間、データを保持しておくことができる変数です。
なので、コンストラクタの処理が走った時に、フィールド変数を初期化してやれば、インスタンスが存在している間
フィールド変数に値が保持されます。
具体的には、下のコードで行くと「yokingaku」という変数がフィールド変数ですが、これに預金額(yokingaku)を設定してやれば、下にあるメインメソッドの処理が終了するまで値が保持されることになります。最後にメモリの解放を行なっているためです。

ただし、Javaにはガベージコレクションという仕組みがあり、わざわざ次のようなコードを書かなくても、メモリの解放を行なってくれます。下ような書き方は明示的なメモリの解放というような言い方をします。

main = null;

ガベージコレクションでのメモリの開放は便利ですが、メモリの解放を行うタイミングは、JVMの都合に依存するので、明示的にメモリを解放した方が、メモリ管理はしやすいです。※必要があればの話ですが。。。

プログラムの内容を見る

では、プログラムを見てみましょう。

<MainBank>

public class MainBank {
    /** 預金額 */
    private int yokingaku;
    /** 処理が何もないものは「デフォルトコンストラクタ」 */
    public MainBank() {
    }

    /** コンストラクタ: 引数あり、フィールドを初期化する */
    public MainBank(int yokingaku) {
        // フィールド変数とコンストラクタの引数名が同じなので区別するためにthisを使う
        this.yokingaku = yokingaku;
    }
}

<メインメソッド>

public static void main(String[] args) {
    // 初期の貯金金額は1000円
    MainBank main = new MainBanik(1000);
    // ATM処理の開始
    main.atm();
    // インスタンスの破棄(メモリの解放)
    main = null;
}

今回は、以前作成したアプリ=「コーダー銀行ATMアプリ」をカスタム(改造)しながら、javaで提供されているAPIの使い方を何個か見てみようと思います。

String

既存のクラスです。「既存」というのはJavaAPIですでに実装済ということです。
主に文字列として使用します。そして、文字列の比較もします。編集、切り出しなどいろいろなメソッドがあるので、一部抜粋して見ます。

  1. equals()メソッド
  2. substring()メソッド
  3. valueOf()メソッド

これらのメソッドはよく使うと思われるので、紹介いたします。
まずは1番から行きます。

String str = "テスト";
if ("テスト".equals(str) {
   System.out.println("テストです。");
}

2番目

String str = "テスト";
String result = str.substring(0, 1);
System.out.println("substring = " + result); // 「テ」と出力される

3番目

int num = 10;
String numStr = String.valueOf(num);
System.out.println(numStr.substring(0,1); // 「1」

3番目は「int型」から「文字列(String)型」へ変換しています。

クラスをカスタム

早速問題を出します。
【問題】
「現在、コンストラクタで預金額を1000に設定しています。
これをコンストラクタに渡した値で預金額を初期化できるように修正するにはどうしたら良いでしょうか?」

ヒント;ソースのダウンロードはこちらからできます

どこを修正すれば良いか?

初めに考えるのは上記の疑問に答えるための答えを考えます。
問題にある「コンストラクタで。。。」という部分に着目します。
そして、コードを見てみると下のようになっています。

/**
 * コンストラクタ
 */
public MainBank() {
    cal = new Calcuration();
}

これでCalcurationクラスを初期化しています。ではコンストラクタの中身は?

public Calcuration() {
    // 預金額は1000円スタート
    yokingaku = 1000;
}

上のようになっています。コンストラクタは引数なしなので、預金額に直接「1000」を代入しています。
ここまで記載すれば、答えは見えてくると思います。

では、次の問題

【問題2】

このアプリにはバグがあります。それを見つけて修正してください。今のところ自分が認識しているのは入力したときにあるバグなのですが、そのほかにもあるかもしれません。
なぜかというと、テストをしていないからです。
本番環境(サーバー)にリリースしようと考えている人は絶対に真似しないようにしてください。テストはちゃんとやりましょう。

この問題の答えは書きません。見つけてみてください。

でわでわ。。。

<<< 前回 次回 >>>