第15週目演習

下準備

テストドライバの導入

第15回目 (秋学期02回目) 分のテストドライバを導入する。以下の手順で行う。

  1. ダウンロード のページを開く (ここをクリック)
  2. プロジェクト「java2007」にある「test」の左側の「+」をクリック
  3. ツリーが展開されるので「install-libraries.xml」を右クリック
  4. 「実行(R)」にマウスカーソルを合わせる
  5. 「1 Ant ビルド」をクリック
  6. 「コンソール」タブに"BUILD SUCCESSFUL"と表示されれば成功
  7. eclipseの画面でプロジェクト「java2007」を右クリック
  8. メニューが表示されるので、「更新」をクリック
  9. "week15.zip" をデスクトップなどにダウンロード
  10. eclipseの画面でプロジェクト「java2007」を右クリック
  11. メニューから「インポート(I)」を選択
  12. 「インポート」ウィンドウが表示されるので、「Zip ファイル」を選択
  13. 「次へ(N)」をクリック
  14. 宛先フォルダー(L): が「java2007」になっていることを確認
  15. From archive file: の右側にある 「参照(R)...」をクリック
  16. ファイルダイアログが表示されるので、ダイアログ内に表示されたダウンロードしたファイルをダブルクリック
  17. 前の画面に戻るので、From archive file: のエリアに正しいパスが入力されていることを確認
  18. フォルダ「/」の左にチェックがついていることを確認 (ついていなければチェックボックスをクリック)
  19. 「警告を出さずに既存リソースを上書き」にチェックがついていることを確認 (上書きしたくないファイルがある場合はチェックを外す)
  20. 「終了 (F)」をクリック

第15週目テストドライバの導入に成功すると、java2007 プロジェクトの test フォルダに j2.lesson02.xml というファイルが作成される。

パッケージの作成

過去の演習を参考にして、「j2.lesson02」というパッケージを作成する。

2次元の座標を表すクラス

2次元の座標 (XY座標) を表すクラス Point を作成する。

このクラスでは、2点間の距離を計算するメソッドを用意することにする。

インスタンスフィールドの抽出

2次元座標インスタンスが持つ属性を考える。今回は直交座標系で点を表すものとして、以下のものを使用することにする。

どちらも double 型として宣言することにする。

コンストラクタの抽出

コンストラクタを考える上で一番簡単な方法は、インスタンスフィールドを全て初期化するコンストラクタについて考えることである。

先ほど、インスタンスフィールドとして、次の2つを抽出した。

これを初期化するコンストラクタとして、引数に x の初期値と y の初期値をとるようなものを考える。

すると、Point(double x, double y) というコンストラクタを用意すればよいことになる。

インスタンスメソッドの抽出

2次元座標のインスタンスメソッドを抽出するには、その「振る舞い」について考えるのであるが、2次元座標はただの概念なので本来は振る舞いを持たない。ここでは練習として、「自分と他の点との距離を求める」メソッドを用意する。

まず、戻り値型は座標を double 型で表しているので double とする。引数は「自分」と「他の点」であるが、インスタンスメソッドで「自分」は this で表せるため、引数にはとらない。そのため、「他の点」のみが引数となる。名前は distance とでもしておく。

上記より、double distance(Point) というメソッドを用意すればよいことになる。注意すべき点として、今回はインスタンスメソッドを用意するので、static をつけてはいけない。

骨格の作成

クラスの作成

以下の手順で、パッケージ「j2.lesson02」に「Point」クラスを作成する。

  1. 先ほど作成したパッケージ 「j2.lesson02」の上で右クリック
  2. マウスカーソルを「新規」に合わせる
  3. 「クラス」をクリック
  4. クラス名は Point とする

インスタンスフィールドの作成

Pointクラス内にインスタンスフィールドを作成する。

package j2.lesson02;

public class Point {
    
    // x 座標
    double x;
    
    // y 座標
    double y;

}

コンストラクタの作成

同様に、コンストラクタを用意する。

コンストラクタは戻り値を取らないため、ダミーの return 文を用意する必要はない。

package j2.lesson02;

public class Point {
    
    // x 座標
    double x;
    
    // y 座標
    double y;
    
    // コンストラクタ
    public Point(double x, double y) {
    }
}

インスタンスメソッドの作成

最後に、インスタンスメソッドを用意する。

戻り値が double 型であるため、return 0.0; というダミーの return 文を用意する。

package j2.lesson02;

public class Point {
    
    // x 座標
    double x;
    
    // y 座標
    double y;
    
    // コンストラクタ
    public Point(double x, double y) {
    }
    
    // 自分と他の点との距離を求める
    public double distance(Point p) {
        return 0.0;
    }
}

骨格テスト

ここまでの作業をCtrl+Sを押して保存し、コンパイルを行う (保存時に自動で行われる)。ここでエラーが発生していたら文法エラーなので見直す。

「Pointに対する骨格テスト」 を実行する。

骨格テストを行った際に緑のバーが表示されれば、外側から見たプログラムの骨格は正しくなっている。

赤いバーが表示された場合、メッセージを元にプログラムを見直すこと。修正を行い、Ctrl+Sで保存した後に「Run」ボタンをクリックする。

メッセージ 詳細
(クラス名), existence j2.lesson02 に対象のクラスが存在していない。パッケージやクラス名を確認
(メンバ名), existence 対象の名前を持つメンバが存在していない
(メソッド名), not static インスタンスメソッドを作る際に static を指定している
(メソッド名), type <T> メソッドを作る際に戻り値の型を間違えている (正しくは <T>)

各種の実装

今回は擬似コードを書くまでもないので、直接プログラムを書く。

擬似コードを書く必要がない理由は、コンストラクタも distance メソッドも、中身が非常に小さいためである。

コンストラクタの実装

コンストラクタは、渡された引数をそのままインスタンスフィールドに代入するだけである。引数とインスタンスフィールドの名前を揃えてあるため、プログラムは非常に簡単である。

public Point(double x, double y) {
    this.x = x;
    this.y = y;
}

先頭に「this.」がついている左辺はフィールドを表し、右辺は引数を表している。

distance メソッドの実装

2点間の距離を求めるには、次の式を使えばよい。


distance メソッドでは自分自身 (x1, y1) が this で、もう一点 (x2, y2) が p であるので、上の数式は次のように読みかえればよい。

すると、次のようなメソッドになる。

// 自分と他の点との距離を求める
public double distance(Point p) {
    double dx = this.x - p.x;
    double dy = this.y - p.y;
    return Math.sqrt(dx * dx + dy * dy);
}

単体テスト

ここまでの作業をCtrl+Sを押して保存し、コンパイルを行う (保存時に自動で行われる)。ここでエラーが発生していたら文法エラーなので見直す。

余裕があれば、JUnitを用いて単体テストを記述せよ。

Point クラス内に mainメソッドを作成し、その中に簡単なテストを記述してみる。例えば、次のような形でよい。

public static void main(String[] args) {
    Point p1 = new Point(1.0, 2.0);
    System.out.println(p1.x + ", " + p1.y);
    Point p2 = new Point(4.0, 6.0);
    System.out.println(p2.x + ", " + p2.y);
    System.out.println(p1.distance(p2));
    System.out.println(p2.distance(p1));
}

このプログラムを実行し、各メソッドが期待通りに動いていることを確認する。確認したら、mainメソッドを消しても良い。

「Pointに対する単体テスト」 を実行する。

単体テストを行った際に緑のバーが表示されれば、各メソッドはある程度正しく作成されていると思われる。

赤いバーが表示された場合、メッセージを元にプログラムを見直すこと。修正を行い、Ctrl+Sで保存した後に「Run」ボタンをクリックする。

メッセージ 詳細
(メソッド名), 期待された結果と異なります (メソッド名)を起動した結果が期待された結果と異なる。下記のテスト項目を参照

テスト項目は以下の通りである。

項目名 詳細
Ctor コンストラクタ(double, double) を (3.0, 4.0) で起動
distance distance メソッドを this = (1.0, 2.0), p = (4.0, 6.0) で起動

Point クラスを使うプログラム

クラスやインスタンスを使用する練習として、Point クラスを使用するプログラムを考える。

擬似コードの作成

「プログラム全体」の擬似コード

プログラム全体の擬似コードは以下のようにする。

プログラム全体
    print "x1を入力:"
    x1 = 入力 (double)
    print "y1を入力:"
    y1 = 入力 (double)
    print "x2を入力:"
    x2 = 入力 (double)
    print "y2を入力:"
    y2 = 入力 (double)
    p1 = (x1, y1)
    p2 = (x2, y2)
    print "(x1, y1)と(x2, y2)の距離は" + p1.distance(p2)

骨格の作成

クラスの作成

以下の手順で、パッケージ「j2.lesson02」に「PointAction」クラスを作成する。

  1. 先ほど作成したパッケージ 「j2.lesson02」の上で右クリック
  2. マウスカーソルを「新規」に合わせる
  3. 「クラス」をクリック
  4. クラス名は PointAction とする

擬似コードの貼り付け (骨格のみ)

作成したクラスに、各擬似コードの名前を貼り付ける。ただし、「プログラム全体」ではコンソールからの入力を行っていたため、import java.io.*; も追加する。

package j2.lesson02;

import java.io.*;

public class PointAction {
    // プログラム全体
}

mainメソッドの作成 (骨格のみ)

擬似コード「プログラム全体」に合わせて、クラス「PointAction」内にpublic static void main(String[] args) から始まるメソッドを作成する。ただし、擬似コード「プログラム全体」ではコンソールからの入力を行っていたので、throws IOExceptionをつける。

全体の骨格

ここまでのプログラムの骨格は以下のようになる。

package j2.lesson02;

import java.io.*;

public class PointAction {

    // プログラム全体
    public static void main(String[] args) throws IOException {
    }
}

骨格テスト

ここまでの作業をCtrl+Sを押して保存し、コンパイルを行う (保存時に自動で行われる)。ここでエラーが発生していたら文法エラーなので見直す。

「PointActionに対する骨格テスト」 を実行する。

骨格テストを行った際に緑のバーが表示されれば、外側から見たプログラムの骨格は正しくなっている。

赤いバーが表示された場合、メッセージを元にプログラムを見直すこと。修正を行い、Ctrl+Sで保存した後に「Run」ボタンをクリックする。

メッセージ 詳細
(クラス名), existence j2.lesson02 に対象のクラスが存在していない。パッケージやクラス名を確認
(メソッド名), existence 指定されたメソッドが存在しない
(メソッド名), public メソッドを作る際に public が抜けている
(メソッド名), static メソッドを作る際に static が抜けている
(メソッド名), type <T> メソッドを作る際に戻り値の型を間違えている (正しくは <T>)
(メソッド名), throws java.io.IOException メソッドを作る際に throws IOException が抜けている

プログラムへの変換

main メソッドの実装

続けて、先ほど作成した PointAction クラスの main メソッドの中身を記述する。

まずは擬似コードをコメントとして貼り付ける。

// プログラム全体
public static void main(String[] args) throws IOException {
    // print "x1を入力:"
    // x1 = 入力 (double)
    // print "y1を入力:"
    // y1 = 入力 (double)
    // print "x2を入力:"
    // x2 = 入力 (double)
    // print "y2を入力:"
    // y2 = 入力 (double)
    // p1 = (x1, y1)
    // p2 = (x2, y2)
    // print "(x1, y1)と(x2, y2)の距離は" + p1.distance(p2)
}

これを元にプログラムを作成する。

// プログラム全体
public static void main(String[] args) throws IOException {
    BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
    
    // print "x1を入力:"
    System.out.print("x1を入力:");
    // x1 = 入力 (double)
    double x1 = Double.parseDouble(reader.readLine());
    
    // print "y1を入力:"
    System.out.print("y1を入力:");
    // y1 = 入力 (double)
    double y1 = Double.parseDouble(reader.readLine());
    
    // print "x2を入力:"
    System.out.print("x2を入力:");
    // x2 = 入力 (double)
    double x2 = Double.parseDouble(reader.readLine());
    
    // print "y2を入力:"
    System.out.print("y2を入力:");
    // y2 = 入力 (double)
    double y2 = Double.parseDouble(reader.readLine());
    
    // p1 = (x1, y1)
    Point p1 = new Point(x1, y1);
    
    // p2 = (x2, y2)
    Point p2 = new Point(x2, y2);
    
    // print "(x1, y1)と(x2, y2)の距離は" + p1.distance(p2)
    System.out.println("(x1, y1)と(x2, y2)の距離は" + p1.distance(p2));
}

全体的には下記のようなプログラムにする。

package j2.lesson02;

import java.io.*;

public class PointAction {
    
    // プログラム全体
    public static void main(String[] args) throws IOException {
        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
        
        // print "x1を入力:"
        System.out.print("x1を入力:");
        // x1 = 入力 (double)
        double x1 = Double.parseDouble(reader.readLine());

        // print "y1を入力:"
        System.out.print("y1を入力:");
        // y1 = 入力 (double)
        double y1 = Double.parseDouble(reader.readLine());
        
        // print "x2を入力:"
        System.out.print("x2を入力:");
        // x2 = 入力 (double)
        double x2 = Double.parseDouble(reader.readLine());
        
        // print "y2を入力:"
        System.out.print("y2を入力:");
        // y2 = 入力 (double)
        double y2 = Double.parseDouble(reader.readLine());
        
        // p1 = (x1, y1)
        Point p1 = new Point(x1, y1);
        
        // p2 = (x2, y2)
        Point p2 = new Point(x2, y2);
        
        // print "(x1, y1)と(x2, y2)の距離は" + p1.distance(p2)
        System.out.println("(x1, y1)と(x2, y2)の距離は" + p1.distance(p2));
    }
}

プログラムの実行

前回と同じ要領でプログラムを実行する。

プログラムの実行に成功し、順に 0.0, 0.0, 3.0, 4.0 と入力すると以下のように表示される。

x1を入力:0.0
y1を入力:0.0
x2を入力:3.0
y2を入力:4.0
(x1, y1)と(x2, y2)の距離は5.0

機能テスト

ここまでの作業をCtrl+Sを押して保存し、コンパイルを行う (保存時に自動で行われる)。ここでエラーが発生していたら文法エラーなので見直す。

「PointActionに対する機能テスト」 を実行する。

赤いバーが表示された場合、メッセージを元にプログラムを見直すこと。修正を行い、Ctrl+Sで保存した後に「Run」ボタンをクリックする。

メッセージ 詳細
期待された結果と異なります 出力された結果が期待された値と異なる。

機能テストの項目

項目名 テストの内容
sample サンプルと同じ入力でプログラムを実行