第18週目課題

課題1801

直角柱を表すクラス Prism をパッケージ j2.lesson05 に作成しなさい。

直角柱とは三角柱や直方体などの多角形を底面に持ち (底面と平行で合同である面をもう一つ持つ)、全ての側面が長方形で構成されているような立体である。

このクラスに次のようなインスタンスフィールドを用意すること。また、private, final として指定すること。

また、次のようなコンストラクタを用意すること。これは public として指定すること。

また、次のようなインスタンスメソッドを用意すること。これらは public として指定すること。

上記に記載されていないインスタンスフィールド、インスタンスメソッド、クラスメソッド、コンストラクタは必要ならば自由に用意して構わない。

手順

指定した箇所で必ずテストを行うこと。

  1. 各メソッドに対する詳細な擬似コードを作成する
  2. パッケージ j2.lesson05 にクラス Prism を作成
  3. 指定したインスタンスメソッドやコンストラクタを作成する
  4. テスト項目「Prismに対する骨格テスト」をパス
  5. インスタンスメソッドやコンストラクタの中身を書く (演習と同様)
  6. main メソッドを書いて簡単なテストを行う
  7. テスト項目「Prismに対する単体テスト」をパス

テストの失敗メッセージ

骨格テスト

メッセージ 詳細
(クラス名), existence パッケージ内に課題で指定したクラスが存在していない。パッケージやクラス名を確認
(クラス名), extends <C> クラスを作る際に継承するクラスが間違っている。正しくは <C>
(メンバ名), existence 指定されたメンバが存在しない
(メンバ名), public メンバを作る際に public の指定がない
(メンバ名), private メンバを作る際に private の指定がない
(メンバ名), final メンバを作る際に final の指定がない
(メンバ名), not static メンバを作る際に static が余計についている
(メンバ名), type <T> メンバを作る際に型の指定を間違っている。正しくは <T>

単体テスト

メッセージ 詳細
期待された結果と異なります (メソッド名)を起動した結果が期待された結果と異なる。テスト項目を参照
未初期化フィールド インスタンスを代入すべきフィールドにインスタンスを代入していない可能性がある

単体テストの項目

テスト失敗時に「Results」の欄の左側に出る「test~」は、テストの項目名を表している。

項目名 詳細
volume_Triangle new Prism(new Triangle(3.0, 4.0, 5.0), 6.0).volume()
surfaceArea_Triangle new Prism(new Triangle(3.0, 4.0, 5.0), 6.0).surfaceArea()
toString_Triangle new Prism(new Triangle(3.0, 4.0, 5.0), 6.0).toString()
volume_Rectangle new Prism(new Rectangle(3.0, 4.0), 6.0).volume()
surfaceArea_Rectangle new Prism(new Rectangle(3.0, 4.0), 6.0).surfaceArea()
toString_Rectangle new Prism(new Rectangle(3.0, 4.0), 6.0).toString()

課題1802

課題1801で作成した Prism クラスを操作するプログラム PrismAction クラスをパッケージ j2.lesson05 に作成しなさい。

このクラスには、mainメソッドだけを用意すればよい。

mainメソッドの動作を表す擬似コードは以下の通りである。出力する文字列などは以下の擬似コードに従うこと。

プログラム全体
   type = 0
   while type が 1 でも 2 でもない
       print "底面の図形を入力(1-三角形,2-長方形):"
       type = コンソール入力 (int)
   Polygon 型で base を宣言
   if 三角形が選択された (type = 1)
       print "三角形が選択されました。", 改行
       print "三角形の辺aの長さを入力:"
       a = コンソール入力 (double)
       print "三角形の辺bの長さを入力:"
       b = コンソール入力 (double)
       print "三角形の辺cの長さを入力:"
       c = コンソール入力 (double)
       base = 辺 a, b, c を持つ新しい三角形インスタンス
   else
       print "長方形が選択されました。", 改行
       print "長方形の底辺の長さを入力:"
       width = コンソール入力 (double)
       print "長方形の高さを入力:"
       height = コンソール入力 (double)
       base = width, height を持つ新しい長方形インスタンス
   print "角柱の高さを入力:"
   height = コンソール入力 (double)
   prism = base, height を持つ新しい角柱インスタンス
   print prism, 改行
   print "体積は" + prism.volume(), 改行
   print "表面積は" + prism.surfaceArea(), 改行

結果の例

入力に順に 1, 12.0, 13.0, 5.0, 3.0 を指定した場合、プログラムを終了まで実行した際のコンソールは以下のようになっている。

底面の図形を入力(1-三角形,2-長方形):1
三角形が選択されました。
三角形の辺aの長さを入力:12.0
三角形の辺bの長さを入力:13.0
三角形の辺cの長さを入力:5.0
角柱の高さを入力:3.0
Prism(Triangle(12.0,13.0,5.0),3.0)
体積は90.0
表面積は150.0

また、入力に順に 2, 3.0, 4.0, 10.0 を指定した場合、プログラムを終了まで実行した際のコンソールは以下のようになっている。

底面の図形を入力(1-三角形,2-長方形):2
長方形が選択されました。
長方形の底辺の長さを入力:3.0
長方形の高さを入力:4.0
角柱の高さを入力:10.0
Prism(Rectangle(3.0,4.0),10.0)
体積は120.0
表面積は164.0

手順

指定した箇所で必ずテストを行うこと。

  1. 各メソッドに対する詳細な擬似コードを作成する
  2. パッケージ j2.lesson05 にクラス PrismAction を作成
  3. クラス内に throws IOException の指定をした mainメソッド (public static void main(String[] args) throws IOException { }) を作成
  4. テスト項目「PrismActionに対する骨格テスト」をパス
  5. 擬似コードをコメントとして貼り付ける
  6. mainメソッドを実装する
  7. 実際に実行して動作を確認する
  8. 機能テスト 「PrismActionに対する機能テスト」 をパス

テストの失敗メッセージ

骨格テスト

メッセージ 詳細
(クラス名), existence パッケージ内に課題で指定したクラスが存在していない。パッケージやクラス名を確認
(メソッド名), existence 指定されたメソッドが存在しない。名前や仮引数の型を確認
(メソッド名), public メソッドを作る際に public が抜けている
(メソッド名), static メソッドを作る際に static が抜けている
(メソッド名), type <T> メソッドを作る際に戻り値型の指定を間違っている。正しくは <T>
(メソッド名), throws ... メソッドを作る際に throws... の指定がない

機能テストの項目

テスト失敗時に「Results」の欄の左側に出る「test~」は、テストの項目名を表している。

項目名 詳細
sample1 1つ目の結果の例と同じ入力
sample2 2つ目の結果の例と同じ入力

課題1803 (optional)

アラームつきの24時間時計を表すクラス AlarmClock18 をパッケージ j2.lesson05 に作成しなさい。

これは第16週目課題の3つ目のプログラム (課題1603) と同様なプログラムであるが、今回は次のクラスを継承して実装すること。

package j2.lesson05;

public class Clock {
    
    // 時刻の内部表現
    private int time;
    
    // 時刻を設定するコンストラクタ
    public Clock(int hour, int minute, int second) {
        this.time = hour * 3600 + minute * 60 + second;
    }
    
    // n 秒進める
    public void tick(int n) {
        this.time += n;
        this.time %= 86400;
    }
    
    // 時刻を秒で取得する
    protected int getTimeAsSec() {
        return this.time;
    }
    
    // 時刻を表示する
    public final void show() {
        // 現在時刻の 時, 分, 秒 を  h, m, s で表すとして 
        // print h + "時" + m + "分" + s + "秒", 改行
        int h = this.time / 3600;
        int m = (this.time / 60) % 60;
        int s = this.time % 60;
        System.out.println(h + "時" + m + "分" + s + "秒");
    }
}

注意すべき点は、protected int getTimeAsSec() メソッドが増えて、void show() メソッドが final を指定しているという点である。protected で指定したメソッドは子クラスからアクセスできる。また、finalで指定したメソッドはオーバーライドできないことに注意すること。

AlarmClock18 は、上記のクラス Clock を継承し、次のようなpublicとして指定されたコンストラクタ、インスタンスメソッドを用意すること。

必要に応じてメソッドをオーバーライドしてもかまわないが、その際にもそれらのメソッドは Clock クラスのコメントに合った振る舞いをすること。つまり、このクラスを Clock として扱った場合 (setAlarmFor を一度も呼び出さなかった場合) に、tick や show が本来の Clock とまったく同じ動作をすること。ただし、時刻を進めたことによって、アラーム時刻を通過したりアラーム時刻に達した場合、アラームを鳴らし(*1)、アラームを解除する。

継承したメソッドを含めて、全ての public なメソッドおよびコンストラクタの動作は第16週目課題の3つ目のプログラム (課題1603) と同等のものであること。

手順

指定した箇所で必ずテストを行うこと。

  1. 各メソッドに対する詳細な擬似コードを作成する
  2. パッケージ j2.lesson05 にクラス AlarmClock18 を作成
  3. クラス内に必要なコンストラクタ、メソッドの骨格を作成
  4. テスト項目「AlarmClock18に対する骨格テスト」をパス
  5. 擬似コードを各メソッドにコメントとして貼り付ける
  6. 擬似コードに対するプログラムを書く (main メソッド以外)
  7. 各メソッドをテストする (JUnitを用いてもよい)
  8. 単体テスト 「AlarmClock18に対する単体テスト」 をパス
  9. mainメソッドを実装する
  10. 実際に実行して動作を確認する

使用例

次のような main メソッドを書いた場合を考える。

// 23時15分
AlarmClock clock = new AlarmClock18(23, 15, 0);
clock.show();

// 現在の時刻にセット
clock.setAlarmFor(23, 15, 0);
// アラームが鳴るはず

// 5時にセット
clock.setAlarmFor(5, 0, 0);

// 2時間進める
clock.tick(2 * 3600);
clock.show();

// 2時間進める
clock.tick(2 * 3600);
clock.show();

// 2時間進める
clock.tick(2 * 3600);
// アラームが鳴るはず
clock.show();

これを実行すると、次のように表示される。

23時15分0秒
アラーム
1時15分0秒
3時15分0秒
アラーム
5時15分0秒

今回は、目覚まし時計ではなくただの時計として扱うこともできる。

// 23時15分
Clock clock = new AlarmClock18(23, 15, 0);
clock.show();
// 2時間進める
clock.tick(2 * 3600);
clock.show();
// 2時間進める
clock.tick(2 * 3600);
clock.show();
// 2時間進める
clock.tick(2 * 3600);
clock.show();

テストの失敗メッセージ

骨格テスト

メッセージ 詳細
(クラス名), existence パッケージ内に課題で指定したクラスが存在していない。パッケージやクラス名を確認
(クラス名), extends <C> クラスを作る際に継承するクラスが間違っている。正しくは <C>
(メンバ名), existence 指定されたメンバが存在しない
(メンバ名), public メンバを作る際に public の指定がない
(メンバ名), private メンバを作る際に private の指定がない
(メンバ名), final メンバを作る際に final の指定がない
(メンバ名), not static メンバを作る際に static が余計についている
(メンバ名), type <T> メンバを作る際に型の指定を間違っている。正しくは <T>

単体テスト

メッセージ 詳細
期待された結果と異なります (メソッド名)を起動した結果が期待された結果と異なる。テスト項目を参照
未初期化フィールド インスタンスを代入すべきフィールドにインスタンスを代入していない可能性がある

単体テストの項目

テスト失敗時に「Results」の欄の左側に出る「test~」は、テストの項目名を表している。

項目名 詳細
bCtor_000 new Clock(0, 0, 0).show()
bCtor_123 new Clock(1, 2, 3).show()
btick_000001 new Clock(0, 0, 0).tick(1).show()
btick_000030 new Clock(0, 0, 0).tick(30).show()
btick_000059 new Clock(0, 0, 0).tick(59).show()
btick_000100 new Clock(0, 0, 0).tick(60).show()
btick_003000 new Clock(0, 0, 0).tick(1800).show()
btick_005959 new Clock(0, 0, 0).tick(3599).show()
btick_010000 new Clock(0, 0, 0).tick(3600).show()
btick_120000 new Clock(0, 0, 0).tick(43200).show()
btick_235959 new Clock(0, 0, 0).tick(86399).show()
btick_loop new Clock(23, 59, 59).tick(1).show()
Ctor_000 new AlarmClock18(0, 0, 0).show()
Ctor_123 new AlarmClock18(1, 2, 3).show()
alarm_0 new AlarmClock18(0, 0, 0), setAlarmFor(0, 0, 0), show()
alarm_1a new AlarmClock18(0, 0, 0), setAlarmFor(0, 0, 1), show()
alarm_1b new AlarmClock18(0, 0, 0), setAlarmFor(0, 0, 1), tick(1), show()
alarm_1c new AlarmClock18(0, 0, 0), setAlarmFor(0, 0, 1), tick(2), show()
alarm_f new AlarmClock18(10, 0, 0), setAlarmFor(15, 0, 0), show()
alarm_fM1 new AlarmClock18(10, 0, 0), setAlarmFor(15, 0, 0), tick(5 * 3600 - 1), show()
alarm_f0 new AlarmClock18(10, 0, 0), setAlarmFor(15, 0, 0), tick(5 * 3600), show()
alarm_f1 new AlarmClock18(10, 0, 0), setAlarmFor(15, 0, 0), tick(5 * 3600 + 1), show()
alarm_b new AlarmClock18(22, 0, 0), setAlarmFor(7, 0, 0), show()
alarm_bM1 new AlarmClock18(22, 0, 0), setAlarmFor(7, 0, 0), tick(9 * 3600 - 1), show()
alarm_b0 new AlarmClock18(22, 0, 0), setAlarmFor(7, 0, 0), tick(9 * 3600), show()
alarm_b1 new AlarmClock18(22, 0, 0), setAlarmFor(7, 0, 0), tick(9 * 3600 + 1), show()
alarm_nextM1 new AlarmClock18(23, 59, 58), setAlarmFor(0, 0, 0), tick(1), show()
alarm_next0 new AlarmClock18(23, 59, 58), setAlarmFor(0, 0, 0), tick(2), show()
alarm_next1 new AlarmClock18(23, 59, 58), setAlarmFor(0, 0, 0), tick(3), show()

課題1804 (optional)

足跡を残すことができる亀を表すクラス TrackTurtle をパッケージ j2.lesson05 に作成しなさい。

TrackTurtle は下記のクラス Turtle を継承すること。これは第15週目演習の4番目のプログラム (課題1504) に似ている。

package j2.lesson05;

public class Turtle {
    
    // x 座標
    private int x;

    // y 座標
    private int y;
    
    // 現在の向きを表す。
    // 0 - North
    // 1 - East
    // 2 - South
    // 3 - West
    private int direction;
    
    // Turtle インスタンスを生成する。
    // 初期値は (x,y)=(0,0), direction=North.
    public Turtle() {
        this.x = 0;
        this.y = 0;
        this.direction = 0;
    }
    
    // 現在向いている方向に1だけ移動する。
    // つまり、以下のように移動する。
    // * North を向いている → y 方向へ +1 移動する
    // * South を向いている → y 方向へ -1 移動する
    // * East を向いている → x 方向へ +1 移動する
    // * West を向いている → x 方向へ -1 移動する
    public Turtle move() {
        switch(this.direction) {
            case 0:
                this.y++;
                break;
            case 1:
                this.x++;
                break;
            case 2:
                this.y--;
                break;
            case 3:
                this.x--;
                break;
            default:
                break;
        }
        return this;
    }
    
    // 90度右へ向く
    public Turtle turnRight() {
        this.direction = (this.direction + 1) % 4;
        return this;
    }
    
    // 90度左へ向く
    public Turtle turnLeft() {
        this.direction = (this.direction + 3) % 4;
        return this;
    }
    
    // 現在のX座標を取得する
    public int getX() {
        return this.x;
    }
    
    // 現在のY座標を取得する
    public int getY() {
        return this.y;
    }
}

TrackTurtle は、上記のクラス Turtle を継承し、次のようなpublicとして指定されたコンストラクタ、インスタンスメソッドを用意すること。

足跡を残す設定のときに move() メソッドが呼び出されると、足跡を残す。詳しくは以下の仕様である。

(*1):下記のような擬似コードで表されるように出力すること

// public void show()
show()
    for y を 0 から 14 まで
        for x を 0 から 14 まで
            if (map(x, y))
                print "@"
            else
                print " " // 半角スペース
        print 改行

// (x, y) における足跡の状態
map(x, y)
    if (x, y) に足跡がある
        return true
    else
        return false

また、TrackTurtle クラス内に簡単な main メソッドを用意し、動作を確認すること。

プログラム例

上記のクラスを使用すると、15x15の領域に簡単な絵を描くことができる。

次の例は、15x15の外周に足跡を残す例である。

TrackTurtle turtle = new TrackTurtle();

// 北西へ
turtle.move().move().move().move().move().move().move();
turtle.turnLeft().move().move().move().move().move().move().move();

// 足跡開始
turtle.setTrackable(true);

// 外周を一周
for (int i = 0; i < 4; i++) {
    turtle.turnLeft();
    for (int j = 0; j < 14; j++) {
        turtle.move();
    }
}

turtle.show();

以下のような結果になる。

@@@@@@@@@@@@@@@
@             @
@             @
@             @
@             @
@             @
@             @
@             @
@             @
@             @
@             @
@             @
@             @
@             @
@@@@@@@@@@@@@@@

また、市松模様と呼ばれる模様も描ける。

TrackTurtle turtle = new TrackTurtle();

// 北西へ
turtle.move().move().move().move().move().move().move();
turtle.turnLeft().move().move().move().move().move().move().move();

turtle.setTrackable(true);

turtle.turnLeft().turnLeft();

// 上から下へ
for (int y = 0; y < 15; y++) {
    // 左から右へ
    for (int x = 0; x < 15; x++) {
        // 現在位置の x + y が偶数なら書き込む
        turtle.setTrackable((x + y) % 2 == 0);
        turtle.move();
    }
    // 左へ帰る
    turtle.setTrackable(false);
    turtle.turnLeft().turnLeft();
    for (int x = 0; x < 15; x++) {
        turtle.move();
    }
    // 一歩下へ
    turtle.turnLeft().move();
    
    // 東を向く
    turtle.turnLeft();
}

turtle.show();
@ @ @ @ @ @ @ @
 @ @ @ @ @ @ @ 
@ @ @ @ @ @ @ @
 @ @ @ @ @ @ @ 
@ @ @ @ @ @ @ @
 @ @ @ @ @ @ @ 
@ @ @ @ @ @ @ @
 @ @ @ @ @ @ @ 
@ @ @ @ @ @ @ @
 @ @ @ @ @ @ @ 
@ @ @ @ @ @ @ @
 @ @ @ @ @ @ @ 
@ @ @ @ @ @ @ @
 @ @ @ @ @ @ @ 
@ @ @ @ @ @ @ @

面白い幾何学模様を描くようなプログラムを考え、作成せよ。

手順

指定した箇所で必ずテストを行うこと。

  1. パッケージ j2.lesson05 にクラス Turtle を作成
  2. 各メソッドに対する詳細な擬似コードを作成する
  3. パッケージ j2.lesson05 にクラス TrackTurtle を作成
  4. 指定したインスタンスメソッドやコンストラクタを作成する
  5. main メソッドを作成する (この時点では空でもよい)
  6. テスト項目「TrackTurtleに対する骨格テスト」をパス
  7. インスタンスメソッドやコンストラクタの中身を書く (演習と同様)
  8. main メソッドを書いて簡単なテストを行う
  9. テスト項目「TrackTurtleに対する機能テスト」をパス

テストの失敗メッセージ

骨格テスト

メッセージ 詳細
(クラス名), existence パッケージ内に課題で指定したクラスが存在していない。パッケージやクラス名を確認
(クラス名), extends <C> クラスを作る際に継承するクラスが間違っている。正しくは <C>
(メンバ名), existence 指定されたメンバが存在しない
(メンバ名), public メンバを作る際に public の指定がない
(メンバ名), private メンバを作る際に private の指定がない
(メンバ名), final メンバを作る際に final の指定がない
(メンバ名), not static メンバを作る際に static が余計についている
(メンバ名), type <T> メンバを作る際に型の指定を間違っている。正しくは <T>

機能テストの項目

テスト失敗時に「Results」の欄の左側に出る「test~」は、テストの項目名を表している。

項目名 詳細
sample1 外周を囲むサンプル
sample2 市松模様を描くサンプル
sample3 sample2と逆の足跡を残す