Back To Main

┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
┃ ◆Javaで珈琲ブレイク vol.010 前編◆
┃……………………………………………………………………………………………
┃ [不定期] まぐまぐ ID=0000088576 Melma! ID=m00061296
┃……………………………………………………………………………………………
┃ 今回からご覧になる方は、バックナンバーご活用下さい
┃ http://www.melma.com/mag/96/m00061296/
┃ http://backno.mag2.com/reader/Back?id=0000088576
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

◆目次◆

◎【前編】
■BankUserInterface.java
■BankUserInterfaceの作り方
■預金・引き出し前の残高表示

【後編】
■預金の問い合わせ(預金なしは0を得る)・残高表示
■引き出しの問い合わせ(引き出しなしは0を得る)・残高表示
■プログラム終了か否か
■IOExceptionのキャッチ
■BankAccountクラスを使わなかった理由
■Question 10


皆さん、こんにちは。

Mr.Hackです。いよいよコンソールベースのユーザーインターフェイス部分に入っていきます。プログラムを作って一番、目に見えるところなので、おもしろいパートだと思います。いろいろとコンソールからタイプして遊んできましょう。

ところで、僕は最近ユーザーインターフェイスは一番最後に作っています。おもしろいものですね。最初にプログラムを始めた頃は、Hello Worldプログラムのように、何かにつけ、コンソール(Dosプロンプト)に表示するようにして、ユーザーインターフェイスがないとどうも落ち着きませんでした。皆さんもたぶんそんな思いをされたとおもいます。BankAccountクラスには、BankUserInterfaceがないとプログラムじゃない、ユーザーインターフェイスを作ってナンボの世界だと思っていたのです。しかし、プログラムになれてくると、logic部分がとてもおもしろくなります。データ構造で、どんなアルゴリズムで実装するかとか、どうやってファイルにデーターを保存するかとか、が気になります。そして、それをしっかり作っておけば、あとは、コンソールベースのUIでしょうが、アプレット・SwingのGUIベースでしょうが、JSP-ServletのWebアプリケーションでしょうが、問題ありません。読者の皆さんは、ユーザーインターフェイスが何であろうとも、何度でも使えるAPIを作って行きましょう。これが、
"Write Once, Run Anywhere"ならぬ"Write Once, Use Anywhere"だとおもいます。

そうなれる日を目指して、まずは、基本的なユーザーインターフェイスである、
コンソールベースのユーザーインターフェイスを見てきます。

まず、バンクシステムの目的からもう一度確認しましょう。このシステムの目的は、ユーザーと預金や引き出しというやり取りをして、残高を保存するプログラムですね。ユーザーはBankUserInterfaceプログラムと対話をして、預金をしたり、引き出しをしたり、残高を参照したりします。


預金・引出・残高参照
1. ユーザー -----------------------> BankUserInterface


ユーザーインターフェイスは、ユーザーからの指示に従い、なんとかして、残高の金額を上げたり、下げたりして、その結果とメッセージをユーザーに表示します。この『何とかして』の部分は、以前作成した、BankAccountクラスのdeposit()、withdraw()、getBalance()メソッドですね。つまり、ユーザーから得た数値をBankAccountのメソッドに渡し、計算してもらいます。


数値と預金・引出・残高参照
2. BankUserInterface ----------------------------> BankAccount

そして、ユーザーインターフェイスはその計算結果をBankAccountから得ます。


計算結果
3. BankUserInterface <---------------------- BankAccount


その結果、ユーザーインターフェイスはユーザーにインストラクション・メッセージや結果を返します。

計算結果・メッセージ
4. ユーザー <------------------------------- BankUserInterface


これが、バンキングプログラムの流れです。BankAccountクラスを作成したので、2と3は終わりです。『数値』を『何らかの方法で』ユーザーから得ることができれば、後は、BankAccountクラスのdeposit()、withdraw()メソッドにその『数値』を渡してあげれば、計算してくれます。その結果を得るには、getBalance()メソッドを使えば得ることが出来ました。

では、その『何らかの方法で』ユーザーから、BankAccountクラスのメソッドに渡せる数値を得える部分をになうのが、BankUserInterfaceクラスです。

これを頭に置きながら、BankUserIntefaceを作っていきましょう。

以下が、前回のQuestion9のサンプル解答です。先週しっかり宿題をされた方だけ、サンプルコードを見て下さい。それ以外の方は、コードをとばして、BankUserInterfaceの作り方からお読み頂いた方がいいかもしれません。

■BankUserInterface.java
━━━━━━━━━━━━ ここから ━━━━━━━━━━━━━━━━━━
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.IOException;

/**
* BankAccountクラスを使用しないコンソールベース・ユーザー・インターフェイス。
* クラス作成の分業を考慮し、BankAccountクラスを使用せずにコンパイル・実行する。
* BankAccountクラス関係の文は記述するが、コメントとしておく。
*
* @author Mr.Hack
* @version 1.0607
* @since 1.0607
*/
public class BankUserInterface {

/**
* コンソールベースのユーザーインターフェイスを実行するメインメソッド。
*
* <PRE>
* 1.残高を表示する。
* 2.コンソールから数値を取得し、その分を預金したと仮定して、残高として表示。
* 3.コンソールから数値を取得し、その文を引き出したと仮定して、残高を表示。
* 4.ユーザーにプログラムを引き続き続けるかどうか問い合わせる。続ける場合、1.に戻る。
* 5.続けない場合、プログラム終了。
* </PRE>
*
* @param args コマンドライン引数。使用せず。
*/
public static void main(String[] args) {
System.out.println(" _________________________________________");
System.out.println("");
System.out.println(" 仮想バンキングへようこそ ");
System.out.println(" _________________________________________");

//コンソールからの入力ストリームを確立し、バッファーとして蓄えられるようにする
BufferedReader buffer = new BufferedReader(new InputStreamReader(System.in));

boolean isContinued = true;

// BankAccountの初期化
//BankAccount bankAccountObject = new BankAccount(0);

// IOExceptionをまとめてキャッチする
try {
// isContinuedがtrueの間、ループする(ユーザーがEnterキーをタイプした場合はループ)
while (isContinued) {

int balance = 0;
System.out.println("");
System.out.println("Call: getBalance()メソッド");
//int balance = (int) bankAccountObject.getBalance();
System.out.println("");
System.out.println("あなたの現在の残高は" + balance + "です");
System.out.println("");
System.out.print("預金額を入力して下さい(預金しない場合は0を入力): ");
// ユーザーが適切な数値(数値でマイナスではない値)を入力するまでループ
while (true) {
try {
// double型の値を取得。数値でない場合、NumberFormatExceptionをスロー
double depositAmount = Double.parseDouble(buffer.readLine());
if (depositAmount != 0) {

// 以下の6行中、BankAccountクラス取得後、3行は削除、
// その後の2行のコメントをとり、最後の1行は削除
System.out.println("");
System.out.println("Call: deposit()メソッド deposit amount:" + depositAmount);
System.out.println("Call: getBalance()メソッド");
// bankAccountObject.deposit(depositAmount);
// balance = (int) bankAccountObject.getBalanace();
balance = (int) depositAmount;

}
break; // 例外が発生しなければ、whileループを抜ける
}
// Double#parseDouble()がスロー
catch (NumberFormatException e) {
System.out.println("");
System.out.print("不正な入力です。数値を入力して下さい : ");
}
// BankAccount#deposit()がスロー
catch (IllegalArgumentException e) {
System.out.println("");
System.out.print(e.getMessage() + "もう一度入力して下さい : ");
}
// BufferedReader#readLine()がスロー
catch (IOException e) {
throw e;
}
}

System.out.println("");
System.out.println("あなたの現在の残高は" + balance + "です");
System.out.println("");
System.out.print("引き出し額を入力して下さい(預金しない場合は0を入力) : ");
// 考え方は、預金の時と同じ
while (true) {
try {
double withdrawAmount = Double.parseDouble(buffer.readLine());
if (withdrawAmount != 0) {

System.out.println("");
System.out.println("Call: withdraw()メソッド withdraw amount:" + withdrawAmount);
System.out.println("Call: getBalance()メソッド");
// bankAccountObject.withdraw(balance);
// balance = (int) bankAccountObject.getBalance();
balance = (int) withdrawAmount;

}
break;
}
catch (NumberFormatException e) {
System.out.println("");
System.out.print("不正な入力です。数値を入力して下さい : ");
}
catch (IOException e) {
throw e;
}
}

System.out.println("");
System.out.println("あなたの現在の残高は" + balance + "です");
System.out.println("");
System.out.println("プログラムを終了する場合は Qを入力して下さい");
System.out.print("引き続き銀行取引を行う場合はそのままEnterキーを押して下さい : ");
while (true) {
try {
char choice = buffer.readLine().charAt(0);
if (choice == 'q' || choice == 'Q') {
isContinued = false;
break; //ユーザーからの値がempty stringではなく、かつqかQの時、whileループを抜ける
}
else {
System.out.print("不正な入力です。QまたはEnterキーを押して下さい : ");
}
}
// String#charAt()がスロー
catch (IndexOutOfBoundsException e) {
isContinued = true;
break;
}
// BufferedReader#readLine()がスロー
catch (IOException e) {
throw e;
}
}

}
}
// BufferedReader#readLine()がスロー
catch (IOException e) {
System.out.println("不正な処理を行ったため、プログラムを終了します");
return;
}
finally {
// ストリームを閉じる
try {
buffer.close();
}
catch (IOException e) {
System.out.println("ストリームをクローズ出来ませんでした");
}
}

System.out.println("ご利用ありがとうございました");
}
}
━━━━━━━━━━━━ ここまで ━━━━━━━━━━━━━━━━━━


■BankUserInterfaceの作り方
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

まずは、プログラムの一番最初と一番最後を考えましょう。ユーザーがプログラムを実行したら、まずはじめに、何をしましょう?

そうプログラムが開始したメッセージがほしいですね。例えば、
┌──────────────────────────────────┐
仮想バンキングシステムへようこそ
└──────────────────────────────────┘

というようなメッセージが最初に表示され、一番最後には、

┌──────────────────────────────────┐
ご利用ありがとうございました
└──────────────────────────────────┘

というようなメッセージが表示されるのがユーザーフレンドリーですね。これによって、ユーザーはプログラムが始まって、プログラムが終わるということがわかります。

では、その最初と最後のメッセージの間には何をしなければならないですか?
もちろん、バンキングシステムなので、最低限、

1.預金
2.引き出し
3.残高参照
4.プログラム終了

のメニューはほしいですね。例えば、1を選べば、預金に関するサブプログラムに移り、ユーザーから数値を得て、deposit()メソッドを呼び出すような感じです。しかしながら、いきなりこのようなメニューを作ると複雑になってしまうので、1から4までを一連の過程でユーザーとやり取りした方が簡単そうです。つまり、

┌──────────────────────────────────┐

預金・引き出し前の残高表示



預金の問い合わせ(預金なしは0を得る)・残高表示



引き出しの問い合わせ(引き出しなしは0を得る)・残高表示



プログラム終了か否か

└──────────────────────────────────┘

とし、プログラムを終了しないならば、また最初の預金の問い合わせに戻る方法です。

ここで、最初の預金の問い合わせに戻る方法は実際にプログラムではどうやって書けばいいでしょうか?

そうです、ループを使えばいいのですね。whileループ使った場合はこんな感じになります。

┌──────────────────────────────────┐

プログラム開始のメッセージ(仮想バンキングシステムへようこそ)

while (ユーザーがプログラムを続行したい){

預金・引き出し前の残高表示

預金の問い合わせ(預金なしは0を得る)・残高表示

引き出しの問い合わせ(引き出しなしは0を得る)・残高表示

プログラム終了か否か

if (ユーザーがプログラムを終了したい) {
whileループを抜け出す
}
else {
while loop続行
}
}

プログラム終了のメッセージ(ご利用ありがとうございました)

└──────────────────────────────────┘

では、預金の問い合わせのところはどんな感じになるでしょうか?

┌──────────────────────────────────┐

while (ユーザーからの数値が適切ではない) {

メッセージ表示(預金額を入力して下さい)
ユーザーから数値を得る
if (その数値が適切な値) {
BankAccount#deposit()を呼ぶ
whileループを抜け出す
}
else {
メッセージ表示(不適切な値です)
whileループ続行
}
}

└──────────────────────────────────┘


ここで、何故ループが必要なのでしょう?もし、ユーザーが数値ではなく、アルファベットを入力したらどうなるでしょう?数値でもマイナスの値を入力したらどうなるでしょう?もう一度ユーザーに入力をうながしたいですね。ですから、ループを使っているのです。

引き出しの問い合わせのところは、BankAccount#withdraw()メソッドを呼び出すところ以外同じロジックです。ここで『#』を使いましたが、BankAccountのwithdraw()メソッドという意味です。この『#』はJava documentに出てきましたね(前号の@seeタグ参照)。これは便利な表現なのでこれを使うことにします。BankAccount.withdraw()と『.』をつかうとBankAccountのwithdraw()メソッドだと分かりますが、これだと、staticなメソッドと勘違いしそうですね。

では、実際にプログラムに取りかかってみましょう。

一番最初と最後に表示するメッセージは、System.out.println()メソッドを使って以下のようになります。

┌──────────────────────────────────┐

System.out.println("_________________________________________");
System.out.println("");
System.out.println(" 仮想バンキングへようこそ ");
System.out.println("_________________________________________");

// while ループ

System.out.println("ご利用ありがとうございました");

└──────────────────────────────────┘


では、while (ユーザーがプログラムを続行したい)はどのようにしますか?

『ユーザーがプログラムを続行したい』部分はboolean型の変数を使ってtrueならwhile続行、falseなら、whileループを抜けるとしましょう。

┌──────────────────────────────────┐

boolean isContinued = true;

while (isContinued) {
//預金・引き出し・残高参照
}

└──────────────────────────────────┘


while文の中では、isContinuedはtrueなのでwhileループを抜けたい場合はisContinuedを

┌──────────────────────────────────┐
isContinued = false;
└──────────────────────────────────┘

とセットすれば、while文の評価の時、whileループから抜け出せます。このようにtrue・falseでスイッチのような役割をするものをトグルと読んだりします。


■預金・引き出し前の残高表示
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

最初の残高の表示はどうしましょう?

┌──────────────────────────────────┐
System.out.println("あなたの現在の残高は" + balance + "です");
└──────────────────────────────────┘

というように表示したいですね。balanceの型は何ですか?double型?いいえ、ここでは、double型はまずいですね。もし最小単位が円だとすると、小数点がつくのはおかしいですね。ATMにいって『あなたの残高は、2000.75円です』といっても75銭はおろせませんからね。切り上げて表示すると銀行が損しますから、ここでは切り捨てます。そうするとint型で良さそうですね。では、int型のbalanceはどこで宣言しますか?whileループの内?外?whileループの内側で宣言したらどうなるでしょう?

┌──────────────────────────────────┐

while (isContinued) {
int balance = 0;
System.out.println("あなたの現在の残高は" + balance + "です");
..
}

└──────────────────────────────────┘


ですね。この場合の短所は、whileループが実行されるごとに、int型変数balanceが作られます。int型は4bytes(8bits x 4 = 32bits)なので毎回、毎回4bytesのメモリースペースが消費されていきます。しかしながら、長所としてbalanceのスコープはwhile文の中だけなので、while文を抜けたとき(ユーザーがプログラム終了を選んだとき)は、balanceはgarbage collectされるのでwhile文の外でbalanceの値を変えることは出来ませんね。balanceは存在しなくなりますから。これは、不意の変数改変を防いでくれます。

一方でbalanceがwhileループの外はどうでしょう?while文の外にあるので、int型のbalanceは一回だけ4bytesのメモリースペースが割り当てられるだけです。これが長所です。しかしながら、短所として、whileループを抜けた後も、balance変数は存続しますので、balanceに新しい値を代入するとbalanceの値が改変されてしまいます。

balance変数はwhileループの中でしか役目がないので(預金・引き出し・残高参照のロジックは、whileループの中にあるから、whileループの外では使う必要はない)、while文の中でいいと思います。つまり、何度もint型のbalanceが作成されメモリースペースが占有されることの短所に目をつぶって、プログラムの他のところで(while文の外で)改変される危険性の回避という長所を選んだことになります。

ところで、実際にBankAccountクラスを使うときは、

┌──────────────────────────────────┐
int balance = (int) bankAccountObject.getBalance();
└──────────────────────────────────┘

という文でbalanceの値を得ます。getBalance()の戻り値はdouble型なのでint型へのキャストが必要です。そのとき小数点以下は切り捨てられます。今回はBankAccountクラスをインポートしたり、同じフォルダに置いたりしない、と想定していますので、この文をコンパイルするとコンパイルエラーになります。そのためこの文はコメントして、

┌──────────────────────────────────┐

while (isContinued) {
int balance = 0;
//int balance = (int) bankAccountObject.getBalance();
System.out.println("あなたの現在の残高は" + balance + "です");
..
}

└──────────────────────────────────┘

とします。実際にBankAccountクラスが使えるようになったら、int balance = 0を削除して、int balance = (int) bankAccountObject.getBalance();のコメントをとります。

引き続き、後編をお楽しみ下さい。


━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
◆Javaで珈琲ブレイク◆
────────────────────────────────────
皆様からの激励・批判のメールをお待ちしております。
【発行者】 Mr.Hack - javacafebreak@hotmail.com
【掲示板】 http://fweb.midi.co.jp/~romanhikou/cafe_entrance.html
※質問は上記の掲示板からどうぞ。
【サイト】 http://mrhack.hoops.ne.jp/
【発行数】 まぐまぐ[902] Melma[140]
【解除】http://mrhack.hoops.ne.jp/
※解除は上記のサイトからいつでもできます。
────────────────────────────────────
(c)2002 MR.HACK ALL RIGHTS RESERVED
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━



© 2002 MR.HACK ALL RIGHTS RESERVED