下準備
テストドライバの導入
第17回目 (秋学期04回目) 分のテストドライバを導入する。以下の手順で行う。
- ダウンロード のページを開く (ここをクリック)
- プロジェクト「java2007」にある「test」の左側の「+」をクリック
- ツリーが展開されるので「install-libraries.xml」を右クリック
- 「実行(R)」にマウスカーソルを合わせる
- 「1 Ant ビルド」をクリック
- 「コンソール」タブに"BUILD SUCCESSFUL"と表示されれば成功
- eclipseの画面でプロジェクト「java2007」を右クリック
- メニューが表示されるので、「更新」をクリック
- "week17.zip" をデスクトップなどにダウンロード
- eclipseの画面でプロジェクト「java2007」を右クリック
- メニューから「インポート(I)」を選択
- 「インポート」ウィンドウが表示されるので、「Zip ファイル」を選択
- 「次へ(N)」をクリック
- 宛先フォルダー(L): が「java2007」になっていることを確認
- From zip file: の右側にある 「参照(R)...」をクリック
- ファイルダイアログが表示されるので、ダイアログ内に表示されたダウンロードしたファイルをダブルクリック
- 前の画面に戻るので、From zip file: のエリアに正しいパスが入力されていることを確認
- フォルダ「/」の左にチェックがついていることを確認 (ついていなければチェックボックスをクリック)
- 「警告を出さずに既存リソースを上書き」にチェックがついていることを確認 (上書きしたくないファイルがある場合はチェックを外す)
- 「終了 (F)」をクリック
第17週目テストドライバの導入に成功すると、java2007 プロジェクトの test フォルダにj2.lesson04.xmlというファイルが作成される。
パッケージの作成
過去の演習を参考にして、「j2.lesson04」というパッケージを作成する。
学生を表すクラス Student の作成
学生を表すクラス Student を j2.lesson04 パッケージ内に作成する。このクラスは、演習の中で親クラスとして利用されるため、以下の内容で必ず作成すること。
package j2.lesson04; // 学生を表すクラス public class Student { // 名前 (通常は変化しないので final) private final String name; // 学籍番号 (通常は変化しないので final) private final String id; // 名前と学籍番号を設定するコンストラクタ public Student(String name, String id) { this.name = name; this.id = id; } // 名前を取得する public String getName() { return this.name; } // 学籍番号を取得する public String getId() { return this.id; } }
クラスの動きを予想できたら、上記をコピーして対象のソースファイルにペーストすればよい。
受講者を表すクラス
ある授業の受講者を表すクラス CourseStudent を作成する。
受講者を表すインスタンスは、次のような特徴を持つことにする。
- 学生である
- 授業の得点 (0~100) を取得できる
- 成績表につけるデータを取得できる
- 形式は 学籍番号,名前,成績
- 成績は授業の得点によって、~59点=D, 60~69点=C, 70~79点=B, 80~点=A
親クラスの抽出
受講者 is-a 学生 より、Student クラスを継承する。
これにより、次のメンバが CourseStudent クラスに引き継がれる。
- public String getName()
- public String getId()
また、コンストラクタが「public Student(String name, String id)」であるため、CourseStudent クラスのコンストラクタから super によって呼び出す必要がある。
インスタンスフィールドの抽出
受講生を表すインスタンスは、次のような特徴があった。
- 授業の得点 (0~100) を取得できる
- 成績表につけるデータを取得できる
- 形式は 学籍番号,名前,成績
そのため、次のようなデータが必要になる
- 得点
- 学籍番号
- 名前
- 成績
しかし、これら全てをインスタンスフィールドとして定義する必要はない。
- 得点: インスタンスフィールドに必要
- 学籍番号: Student インスタンスの getId() で取得可能であるため不要
- 名前: Student インスタンスの getName() で取得可能であるため不要
- 成績: 得点から計算可能であるため不要
上記より、int 型の score というインスタンスフィールドを用意し、このフィールドが不正に参照されないように private を指定する。また、得点を書き換える機能を持たないため、final も指定する。
まとめると、次の特徴からインスタンスフィールドを抽出した。
- 学生である (-> extends Student)
- 授業の得点 (0~100) を取得できる -> private final int score
- 成績表につけるデータを取得できる (-> 他から取得可能)
- 形式は 学籍番号,名前,成績
- 成績は授業の得点によって、~59点=D, 60~69点=C, 70~79点=B, 80~点=A
コンストラクタの抽出
次のようなコンストラクタを用意する。
- public CourseStudent(String name, String id, int score)
- 名前、学籍番号、得点を指定してインスタンスを作成する。
- 引数 name: 受講者の名前
- 引数 id: 受講者の学籍番号
- 引数 score: 受講者の得点
インスタンスフィールドの初期化に必要な情報は得点のみであったが、親クラスのインスタンスフィールドの初期化に名前と学籍番号が必要である。そのため、上記のようなコンストラクタになる。
インスタンスメソッドの抽出
受講者を表すインスタンスの特徴より、インスタンスメソッドを抽出する。
- 学生である
- 授業の得点 (0~100) を取得できる -> public int getScore()
- 成績表につけるデータを取得できる -> public String getPerformance()
- 形式は 学籍番号,名前,成績
- 成績は授業の得点によって、~59点=D, 60~69点=C, 70~79点=B, 80~点=A
上記より、インスタンスメソッドの仕様を決定する。
- public int getScore()
- 授業の得点 (0~100) を取得する
- 戻り値: 受講者の得点
- public String getPerformance()
- 成績表につけるデータを取得する
- 戻り値: 受講者の成績データ
骨格の作成
クラスの作成
以下の手順で、パッケージ「j2.lesson04」に「CourseStudent」クラスを作成する。
- 先ほど作成したパッケージ 「j2.lesson04」の上で右クリック
- マウスカーソルを「新規」に合わせる
- 「クラス」をクリック
- クラス名は CourseStudent とする
親クラスの設定
CourseStudent に Student クラスを継承させる。
クラス名の後に extends Student を付け加えればよい。
package j2.lesson04; // 受講者を表すクラス public class CourseStudent extends Student { }
インスタンスフィールドの作成
CourseStudent クラス内にインスタンスフィールドを作成する。
package j2.lesson04; // 受講者を表すクラス public class CourseStudent extends Student { // 得点 private final int score; }
コンストラクタの作成
同様に、コンストラクタを用意する。Student クラスのコンストラクタが「Student(String name, String id)」であるため、super(name, id); によって親クラスに名前と学籍番号の情報を与える。
また、 score を final で宣言したため、コンストラクタ内で初期化する。
コンストラクタは戻り値を取らないため、ダミーの return 文を用意する必要はない。
package j2.lesson04; // 受講者を表すクラス public class CourseStudent extends Student { // 得点 private final int score; // 受講者を表すインスタンスを作成する public CourseStudent(String name, String id, int score) { super(name, id); this.score = score; } }
インスタンスメソッドの作成
最後に、インスタンスメソッドを用意する。
ダミーの return 文を追加する。String 型を返すメソッドでは、空の文字列 "" を return するか、「無意味なもの」を表す null を返せばよい。ここでは空の文字列を返しておく。
package j2.lesson04; // 受講者を表すクラス public class CourseStudent extends Student { // 得点 private final int score; // 受講者を表すインスタンスを作成する public CourseStudent(String name, String id, int score) { super(name, id); this.score = score; } // 得点を取得する public int getScore() { return 0; } // 成績情報を文字列で返す // 学籍番号 + "," + 名前 + "," + rank public String getPerformance() { return ""; } }
骨格テスト
ここまでの作業をCtrl+Sを押して保存し、コンパイルを行う (保存時に自動で行われる)。ここでエラーが発生していたら文法エラーなので見直す。
「CourseStudentに対する骨格テスト」を実行する。
骨格テストを行った際に緑のバーが表示されれば、外側から見たプログラムの骨格は正しくなっている。
赤いバーが表示された場合、メッセージを元にプログラムを見直すこと。修正を行い、Ctrl+Sで保存した後に「Run」ボタンをクリックする。
メッセージ | 詳細 |
---|---|
(クラス名), existence | j2.lesson04 に対象のクラスが存在していない。パッケージやクラス名を確認 |
(クラス名), extends <C> | クラスが正しいクラスを継承していない (正しくは extends <C>) |
(メンバ名), existence | 対象の名前を持つメンバが存在していない |
(メンバ名), public | 対象のメンバが public で宣言されていない |
(メンバ名), private | 対象のメンバが private で宣言されていない |
(メソッド名), not static | インスタンスメソッドを作る際に static を指定している |
(メソッド名), type <T> | メソッドを作る際に戻り値の型を間違えている (正しくは <T>) |
各種の実装
今回も擬似コードを書くまでもないので、直接プログラムを書く。
コンストラクタの実装
コンストラクタでは、CourseStudent のインスタンスフィールドを設定する。name, id は Student インスタンスのフィールドなので、ここでは score のみ設定する。つまり、骨格と同様である。
// 受講者を表すインスタンスを作成する public CourseStudent(String name, String id, int score) { super(name, id); this.score = score; }
getScore メソッドの実装
getScore メソッドでは、単に インスタンスフィールドの score を返すだけである。インスタンスフィールドは外部から不正に変更されたくないため public で宣言し、getScore() というメソッドを用意して値を取得している。
// 得点を取得する public int getScore() { return this.score; }
このように、フィールドの値を取得するためのメソッドを一般的にgetterと呼び、「get + フィールド名」の名前を持たせる。
getPerformance メソッドの実装
getPerformance メソッドでは、成績情報を「学籍番号 + "," + 名前 + "," + rank」の形式の文字列で返す。それぞれの情報は次のように用意すればよい。
- 学籍番号 -> Student クラスを継承しているため、this.getId() で取得できる
- 名前 -> Student クラスを継承しているため、this.getName() で取得できる
- rank -> this.score から算出する
// 成績情報を文字列で返す // 学籍番号 + "," + 名前 + "," + rank public String getPerformance() { // score = ~59 : D // score = 60~69 : C // score = 70~79 : B // score = 80~ : A String rank; if (this.score <= 59) { rank = "D"; } else if (this.score <= 69) { rank = "C"; } else if (this.score <= 79) { rank = "B"; } else { rank = "A"; } // 学籍番号 + "," + 名前 + "," + rank // this を省略して そのまま get~() でもよい。 return this.getId() + "," + this.getName() + "," + rank; }
単体テスト
ここまでの作業をCtrl+Sを押して保存し、コンパイルを行う (保存時に自動で行われる)。ここでエラーが発生していたら文法エラーなので見直す。
余裕があれば、JUnitを用いて単体テストを記述せよ。
CourseStudent クラス内に mainメソッドを作成し、その中に簡単なテストを記述してみる。例えば、次のような形でよい。
// テストプログラム public static void main(String[] args) { CourseStudent student = new CourseStudent("ほげ", "n99k999", 100); System.out.println(student.getName()); System.out.println(student.getId()); System.out.println(student.getScore()); System.out.println(student.getPerformance()); }
このプログラムを実行し、各メソッドが期待通りに動いていることを確認する。確認したら、mainメソッドを消しても良い。
「CourseStudentに対する単体テスト」を実行する。
単体テストを行った際に緑のバーが表示されれば、各メソッドはある程度正しく作成されていると思われる。
赤いバーが表示された場合、メッセージを元にプログラムを見直すこと。修正を行い、Ctrl+Sで保存した後に「Run」ボタンをクリックする。
メッセージ | 詳細 |
---|---|
(メソッド名), 期待された結果と異なります | (メソッド名)を起動した結果が期待された結果と異なる。下記のテスト項目を参照 |
テスト項目は以下の通りである。
項目名 | 詳細 |
---|---|
getPerformance_a | new CourseStudent("a", "b", 0).getPerformance() |
getPerformance_b | new CourseStudent("c", "d", 30).getPerformance() |
getPerformance_c | new CourseStudent("e", "f", 59).getPerformance() |
getPerformance_d | new CourseStudent("g", "h", 60).getPerformance() |
getPerformance_e | new CourseStudent("i", "j", 65).getPerformance() |
getPerformance_f | new CourseStudent("k", "l", 69).getPerformance() |
getPerformance_g | new CourseStudent("m", "n", 70).getPerformance() |
getPerformance_h | new CourseStudent("o", "p", 75).getPerformance() |
getPerformance_i | new CourseStudent("q", "r", 79).getPerformance() |
getPerformance_j | new CourseStudent("s", "t", 80).getPerformance() |
getPerformance_k | new CourseStudent("u", "v", 90).getPerformance() |
getPerformance_perfect | new CourseStudent("ほげ", "n99k9999", 100).getPerformance() |
プログラム全体
CourseStudentクラス全体を掲載しておく。
package j2.lesson04; // 受講者を表すクラス public class CourseStudent extends Student { // 得点 private final int score; // 受講者を表すインスタンスを作成する public CourseStudent(String name, String id, int score) { super(name, id); this.score = score; } // 得点を取得する public int getScore() { return this.score; } // 成績情報を文字列で返す // 学籍番号 + "," + 名前 + "," + rank public String getPerformance() { // score = ~59 : D // score = 60~69 : C // score = 70~79 : B // score = 80~ : A String rank; if (this.score <= 59) { rank = "D"; } else if (this.score <= 69) { rank = "C"; } else if (this.score <= 79) { rank = "B"; } else { rank = "A"; } // 学籍番号 + "," + 名前 + "," + rank // this を省略して そのまま get~() でもよい。 return this.getId() + "," + this.getName() + "," + rank; } }
CourseStudent クラスを使うプログラム
CourseStudent クラスを使用するプログラムを考える。
擬似コードの作成
「プログラム全体」の擬似コード
プログラム全体の擬似コードは以下のようにする。
全体の処理 print "名前を入力:" name = コンソール入力 (String) print "学籍番号を入力:" id = コンソール入力 (String) print "得点を入力:" score = コンソール入力 (int) student = CourceStudent 型の新しいインスタンス ただし、name, id, score で初期化 print student.getPerformance()
骨格の作成
クラスの作成
以下の手順で、パッケージ「j2.lesson04」に「CourseStudentAction」クラスを作成する。
- 先ほど作成したパッケージ 「j2.lesson04」の上で右クリック
- マウスカーソルを「新規」に合わせる
- 「クラス」をクリック
- クラス名は CourseStudentAction とする
擬似コードの貼り付け (骨格のみ)
作成したクラスに、各擬似コードの名前を貼り付ける。ただし、「プログラム全体」ではコンソールからの入力を行っていたため、import java.io.*; も追加する。
package j2.lesson04; import java.io.*; public class CourseStudentAction { // プログラム全体 }
mainメソッドの作成 (骨格のみ)
擬似コード「プログラム全体」に合わせて、クラス「CourseStudentAction」内にpublic static void main(String[] args) から始まるメソッドを作成する。ただし、擬似コード「プログラム全体」ではコンソールからの入力を行っていたので、throws IOExceptionをつける。
全体の骨格
ここまでのプログラムの骨格は以下のようになる。
package j2.lesson04; import java.io.*; public class CourseStudentAction { // プログラム全体 public static void main(String[] args) throws IOException { } }
骨格テスト
ここまでの作業をCtrl+Sを押して保存し、コンパイルを行う (保存時に自動で行われる)。ここでエラーが発生していたら文法エラーなので見直す。
「CourseStudentActionに対する骨格テスト」を実行する。
骨格テストを行った際に緑のバーが表示されれば、外側から見たプログラムの骨格は正しくなっている。
赤いバーが表示された場合、メッセージを元にプログラムを見直すこと。修正を行い、Ctrl+Sで保存した後に「Run」ボタンをクリックする。
メッセージ | 詳細 |
---|---|
(クラス名), existence | j2.lesson04 に対象のクラスが存在していない。パッケージやクラス名を確認 |
(メソッド名), existence | 指定されたメソッドが存在しない |
(メソッド名), public | メソッドを作る際に public が抜けている |
(メソッド名), static | メソッドを作る際に static が抜けている |
(メソッド名), type <T> | メソッドを作る際に戻り値の型を間違えている (正しくは <T>) |
(メソッド名), throws java.io.IOException | メソッドを作る際に throws IOException が抜けている |
継承の確認
プログラムとは関係ないが、継承によって親クラスで定義したインスタンスメソッドを、子クラスのインスタンスから使用できることを確認する。
また、拡大変換が可能であることを確認する。
CourseStudentActionクラスのmainメソッドに次のようなプログラムを書き、どのようなことが起こるか確認せよ。
package j2.lesson04; import java.io.*; public class CourseStudentAction { // 全体の処理 public static void main(String[] args) throws IOException { CourseStudent cs = new CourseStudent("hoge", "m00k0000"); // 親クラスからの継承確認 System.out.println(cs.getName()); System.out.println(cs.getId()); // 拡大変換 Student s = new CourseStudent("foo", "n99k9999"); System.out.println(s.getName()); System.out.println(s.getId()); // これはできない (Student に getScore メソッドがない) // System.out.println(s.getScore()); } }
確認したらmainの中身を空にしてかまわない。
プログラムへの変換
main メソッドの実装
続けて、先ほど作成した CourseStudentAction クラスの main メソッドの中身を記述する。
まずは擬似コードをコメントとして貼り付ける。
// 全体の処理 public static void main(String[] args) throws IOException { // print "名前を入力:" // name = コンソール入力 (String) // print "学籍番号を入力:" // id = コンソール入力 (String) // print "得点を入力:" // score = コンソール入力 (int) // student = CourceStudent 型の新しいインスタンス // ただし、name, id, score で初期化 // print student.getPerformance() }
これを元にプログラムを作成する。
// 全体の処理 public static void main(String[] args) throws IOException { BufferedReader reader = new BufferedReader(new InputStreamReader(System.in)); // print "名前を入力:" System.out.print("名前を入力:"); // name = コンソール入力 (String) String name = reader.readLine(); // print "学籍番号を入力:" System.out.print("学籍番号を入力:"); // id = コンソール入力 (String) String id = reader.readLine(); // print "得点を入力:" System.out.print("得点を入力:"); // score = コンソール入力 (int) int score = Integer.parseInt(reader.readLine()); // student = CourceStudent 型の新しいインスタンス // ただし、name, id, score で初期化 CourseStudent student = new CourseStudent(name, id, score); // print student.getPerformance() System.out.println(student.getPerformance()); }
全体的には下記のようなプログラムにする。
package j2.lesson04; import java.io.*; public class CourseStudentAction { // 全体の処理 public static void main(String[] args) throws IOException { BufferedReader reader = new BufferedReader(new InputStreamReader(System.in)); // print "名前を入力:" System.out.print("名前を入力:"); // name = コンソール入力 (String) String name = reader.readLine(); // print "学籍番号を入力:" System.out.print("学籍番号を入力:"); // id = コンソール入力 (String) String id = reader.readLine(); // print "得点を入力:" System.out.print("得点を入力:"); // score = コンソール入力 (int) int score = Integer.parseInt(reader.readLine()); // student = CourceStudent 型の新しいインスタンス // ただし、name, id, score で初期化 CourseStudent student = new CourseStudent(name, id, score); // print student.getPerformance() System.out.println(student.getPerformance()); } }
プログラムの実行
前回と同じ要領でプログラムを実行する。
プログラムの実行に成功し、順に ほげ, n99k9999, 78 と入力すると以下のように表示される。
名前を入力:ほげ 学籍番号を入力:n99k9999 得点を入力:78 n99k9999,ほげ,B
機能テスト
ここまでの作業をCtrl+Sを押して保存し、コンパイルを行う (保存時に自動で行われる)。ここでエラーが発生していたら文法エラーなので見直す。
「CourseStudentActionに対する機能テスト」を実行する。
赤いバーが表示された場合、メッセージを元にプログラムを見直すこと。修正を行い、Ctrl+Sで保存した後に「Run」ボタンをクリックする。
メッセージ | 詳細 |
---|---|
期待された結果と異なります | 出力された結果が期待された値と異なる。 |
機能テストの項目
項目名 | テストの内容 |
---|---|
sample | サンプルと同じ入力でプログラムを実行 |