6月1日

テスト駆動開発で自動販売機を作る

前回は10円玉の釣り銭を使い果たすテストを用意したところまで。
テストを実行すると失敗するので、テストにパスするよう実装する。

VendingMachineTest.java

 
	@Test
	public void testChangeStock6() {
		VendingMachine vm = new VendingMachine();
		for (int i = 0; i < 3; i++) {
			vm.throwInto(100);
			vm.throwInto(50);
			vm.purchace();
		}
		int s10 = vm.get10YenStock();
		assertThat(s10, is(1));
		vm.throwInto(100);
		vm.throwInto(50);
		Output o = vm.purchace();
		s10 = vm.get10YenStock();
		assertThat(s10, is(1));
		assertThat(o.getJuiceCount(), is(0));
		assertThat(o.getChange(), is(0));
		int total = vm.getTotal();
		assertThat(total, is(150));
	}

お釣りが足りないときは購入できないようにすればいい。ということは、購入可能かどうかを調べているisPurchacable()メソッド内でお釣りが足りるかどうかを確認すればいい。

そこで、お釣りが足りるかどうかを調べる isChangeShorted()メソッドを追加する。お釣りが足りるならfalse、足りないならtrueを返す。

常にfalseを返すように作れば、従来どおりの動作となる。

	public boolean isPurchacable() {
		if (getJuiceStock() == 0) return false;
		if (total < getJuicePrice()) return false;
		if (isChangeShorted()) return false;
		return true;
	}

	private boolean isChangeShorted() {
		return false;
	}

お釣りが50円よりも少ないときは10円玉のお釣りを出す必要がある。
10円玉のストックとお釣りの金額を比較して、釣り銭が足りるかどうかを調べる。

	private boolean isChangeShorted() {
		int change = total - getJuicePrice();
		if (change < 50) {
			if (stock10yen * 10 < change) return true;
		}
		return false;
	}

100円玉の釣り銭が足りなくなるテストを作る。
VendingMachineTest.java

	@Test
	public void testChangeStock7() {
		VendingMachine vm = new VendingMachine();
		for (int i = 0; i < 2; i++) {
			vm.throwInto(500);
			vm.throwInto(10);
			vm.throwInto(10);
			vm.purchace();
		}
		int s100 = vm.get100YenStock();
		assertThat(s100, is(2));
		vm.throwInto(500);
		Output o = vm.purchace();
		s100 = vm.get100YenStock();
		assertThat(s100, is(2));
		assertThat(o.getJuiceCount(), is(0));
		assertThat(o.getChange(), is(0));
	}

10円玉の釣り銭不足と同様の実装を追加する。

VendingMachine.java

	private boolean isChangeShorted() {
		int change = total - getJuicePrice();
		if (change < 50) {
			if (stock10yen * 10 < change) return true;
		}
		if (change < 500) {
			if (stock100yen * 100 < change) return true;
		}
		return false;
	}

これを追加すると、testPurchace1000() が通らなくなってしまった!
テストコードを確認してみると、お釣りのチェックをしなければパスするコードを書いていることがわかる。
このテストでは、ジュースの在庫がなくなるのを確認したいので、お釣りが不要になるようにテストコードを修正する。

VendingMachineTest.java

	@Test
	public void testPurchace1000() {
		VendingMachine vm = new VendingMachine();
		vm.throwInto(1000);
		Output o = vm.purchace();
		assertThat(o.getJuiceCount(), is(1));
		assertThat(o.getChange(), is(880));
		int stock = vm.getJuiceStock();
		assertThat(stock, is(4));
		for (int i = 0; i < 4; i++) {
			//vm.throwInto(500); 500円ではなく、釣り銭が出ない120円で購入
			vm.throwInto(100);
			vm.throwInto(10);
			vm.throwInto(10);
			o = vm.purchace();
		}
		stock = vm.getJuiceStock();
		assertThat(stock, is(0));
		vm.throwInto(500);
		o = vm.purchace();
		assertThat(o.getJuiceCount(), is(0));
		assertThat(o.getChange(), is(0));
		int total = vm.getTotal();
		assertThat(total, is(500));
	}

50円玉の釣り銭不足になるテストを作りたいが、ジュースの在庫が5個しかないのでできない。
500円玉の釣り銭不足も同様。
ここまでで、テスト駆動開発の実習を終わりにする。

VendingMachineTest.java

import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;

import org.junit.Test;


public class VendingMachineTest {

	@Test
	public void testThrowInto() {
		VendingMachine vm = new VendingMachine();
		int change = vm.throwInto(10);
		assertThat(change, is(0));
		int total = vm.getTotal();
		assertThat(total, is(10));
		change = vm.throwInto(50);
		assertThat(change, is(0));
		total = vm.getTotal();
		assertThat(total, is(60));
	}

	@Test
	public void testThrowIntoNotAccept() {
		VendingMachine vm = new VendingMachine();
		int change = vm.throwInto(1);
		assertThat(change, is(1));
		int total = vm.getTotal();
		assertThat(total, is(0));
		change = vm.throwInto(5);
		assertThat(change, is(5));
		change = vm.throwInto(2000);
		assertThat(change, is(2000));
		change = vm.throwInto(5000);
		assertThat(change, is(5000));
		change = vm.throwInto(10000);
		assertThat(change, is(10000));
	}

	@Test
	public void testThrowIntoRefund() {
		VendingMachine vm = new VendingMachine();
		int change = vm.throwInto(10);
		assertThat(change, is(0));
		int total = vm.getTotal();
		assertThat(total, is(10));
		int refund = vm.refund();
		assertThat(refund, is(10));
		total = vm.getTotal();
		assertThat(total, is(0));
		change = vm.throwInto(100);
		assertThat(change, is(0));
		total = vm.getTotal();
		assertThat(total, is(100));
		change = vm.throwInto(500);
		assertThat(change, is(0));
		total = vm.getTotal();
		assertThat(total, is(600));
		refund = vm.refund();
		assertThat(refund, is(600));
	}

	@Test
	public void testGetJuice() {
		VendingMachine vm = new VendingMachine();
		String name = vm.getJuiceName();
		assertThat(name, is("コーラ"));
		int price = vm.getJuicePrice();
		assertThat(price, is(120));
		int stock = vm.getJuiceStock();
		assertThat(stock, is(5));
	}

	@Test
	public void testIsPurchacable() {
		VendingMachine vm = new VendingMachine();
		boolean b = vm.isPurchacable();
		assertThat(b, is(false));
		vm.throwInto(500);
		b = vm.isPurchacable();
		assertThat(b, is(true));
	}

	@Test
	public void testPurchace() {
		VendingMachine vm = new VendingMachine();
		vm.throwInto(100);
		vm.throwInto(10);
		vm.throwInto(10);
		Output o = vm.purchace();
		assertThat(o.getJuiceCount(), is(1));
		assertThat(o.getChange(), is(0));
		int total = vm.getTotal();
		assertThat(total, is(0));
	}

	@Test
	public void testPurchace500() {
		VendingMachine vm = new VendingMachine();
		vm.throwInto(500);
		Output o = vm.purchace();
		assertThat(o.getJuiceCount(), is(1));
		assertThat(o.getChange(), is(380));
		int total = vm.getTotal();
		assertThat(total, is(0));
	}

	@Test
	public void testPurchace1000() {
		VendingMachine vm = new VendingMachine();
		vm.throwInto(1000);
		Output o = vm.purchace();
		assertThat(o.getJuiceCount(), is(1));
		assertThat(o.getChange(), is(880));
		int stock = vm.getJuiceStock();
		assertThat(stock, is(4));
		for (int i = 0; i < 4; i++) {
			vm.throwInto(100);
			vm.throwInto(10);
			vm.throwInto(10);
			o = vm.purchace();
		}
		stock = vm.getJuiceStock();
		assertThat(stock, is(0));
		vm.throwInto(500);
		o = vm.purchace();
		assertThat(o.getJuiceCount(), is(0));
		assertThat(o.getChange(), is(0));
		int total = vm.getTotal();
		assertThat(total, is(500));
	}

	@Test
	public void testSale() {
		VendingMachine vm = new VendingMachine();
		int sale = vm.getSale();
		assertThat(sale, is(0));
		vm.throwInto(500);
		vm.purchace();
		sale = vm.getSale();
		assertThat(sale, is(120));
	}

	@Test
	public void testChangeStock() {
		VendingMachine vm = new VendingMachine();
		int s10 = vm.get10YenStock();
		assertThat(s10, is(10));
		int s50 = vm.get50YenStock();
		assertThat(s50, is(10));
		int s100 = vm.get100YenStock();
		assertThat(s100, is(10));
		int s500 = vm.get500YenStock();
		assertThat(s500, is(10));
		vm.throwInto(100);
		vm.throwInto(50);
		Output o = vm.purchace();
		assertThat(o.getJuiceCount(), is(1));
		assertThat(o.getChange(), is(30));
		s10 = vm.get10YenStock();
		assertThat(s10, is(7));
	}

	@Test
	public void testChangeStock2() {
		VendingMachine vm = new VendingMachine();
		vm.throwInto(100);
		vm.throwInto(50);
		Output o = vm.purchace();
		assertThat(o.getJuiceCount(), is(1));
		assertThat(o.getChange(), is(30));
		int s10 = vm.get10YenStock();
		assertThat(s10, is(7));

		vm.throwInto(100);
		vm.throwInto(50);
		vm.purchace();
		s10 = vm.get10YenStock();
		assertThat(s10, is(4));
	}

	@Test
	public void testChangeStock3() {
		VendingMachine vm = new VendingMachine();
		vm.throwInto(100);
		vm.throwInto(100);
		vm.purchace();
		int s10 = vm.get10YenStock();
		assertThat(s10, is(7));
		int s50 = vm.get50YenStock();
		assertThat(s50, is(9));
	}

	@Test
	public void testChangeStock4() {
		VendingMachine vm = new VendingMachine();
		vm.throwInto(500);
		vm.purchace();
		int s10 = vm.get10YenStock();
		assertThat(s10, is(7));
		int s50 = vm.get50YenStock();
		assertThat(s50, is(9));
		int s100 = vm.get100YenStock();
		assertThat(s100, is(7));
	}

	@Test
	public void testChangeStock5() {
		VendingMachine vm = new VendingMachine();
		vm.throwInto(1000);
		vm.purchace();
		int s10 = vm.get10YenStock();
		assertThat(s10, is(7));
		int s50 = vm.get50YenStock();
		assertThat(s50, is(9));
		int s100 = vm.get100YenStock();
		assertThat(s100, is(7));
		int s500 = vm.get500YenStock();
		assertThat(s500, is(9));
	}

	@Test
	public void testChangeStock6() {
		VendingMachine vm = new VendingMachine();
		for (int i = 0; i < 3; i++) {
			vm.throwInto(100);
			vm.throwInto(50);
			vm.purchace();
		}
		int s10 = vm.get10YenStock();
		assertThat(s10, is(1));
		vm.throwInto(100);
		vm.throwInto(50);
		Output o = vm.purchace();
		s10 = vm.get10YenStock();
		assertThat(s10, is(1));
		assertThat(o.getJuiceCount(), is(0));
		assertThat(o.getChange(), is(0));
		int total = vm.getTotal();
		assertThat(total, is(150));
	}

	@Test
	public void testChangeStock7() {
		VendingMachine vm = new VendingMachine();
		for (int i = 0; i < 2; i++) {
			vm.throwInto(500);
			vm.throwInto(10);
			vm.throwInto(10);
			vm.purchace();
		}
		int s100 = vm.get100YenStock();
		assertThat(s100, is(2));
		vm.throwInto(500);
		Output o = vm.purchace();
		s100 = vm.get100YenStock();
		assertThat(s100, is(2));
		assertThat(o.getJuiceCount(), is(0));
		assertThat(o.getChange(), is(0));
	}
}

VendingMachine.java


public class VendingMachine {
	private int total = 0;
	private int stock = 5;
	private int sale = 0;
	private int stock10yen = 10;
	private int stock50yen = 10;
	private int stock100yen = 10;
	private int stock500yen = 10;

	public int throwInto(int yen) {
		if (yen == 1) return 1;
		if (yen == 5) return 5;
		if (yen == 2000) return 2000;
		if (yen == 5000) return 5000;
		if (yen == 10000) return 10000;
		total += yen;
		return 0;
	}

	public int getTotal() {
		return total;
	}

	public int refund() {
		int refund = total;
		total = 0;
		return refund;
	}

	public String getJuiceName() {
		return "コーラ";
	}

	public int getJuicePrice() {
		return 120;
	}

	public int getJuiceStock() {
		return stock;
	}

	public boolean isPurchacable() {
		if (getJuiceStock() == 0) return false;
		if (total < getJuicePrice()) return false;
		if (isChangeShorted()) return false;
		return true;
	}

	private boolean isChangeShorted() {
		int change = total - getJuicePrice();
		if (change < 50) {
			if (stock10yen * 10 < change) return true;
		}
		if (change < 500) {
			if (stock100yen * 100 < change) return true;
		}
		return false;
	}

	public Output purchace() {
		if (!isPurchacable()) {
			return new Output(0, 0);
		}
		total -= getJuicePrice();
		int change = total;
		decreaseStock(change);
		total -= change;
		stock--;
		sale += getJuicePrice();
		return new Output(1, change);
	}

	private void decreaseStock(int change) {
		if (change >= 500) {
			change -= 500;
			stock500yen--;
		}
		if (change >= 100) {
			int c = change / 100;
			change -= c * 100;
			stock100yen -= c;
		}
		if (change >= 50) {
			change -= 50;
			stock50yen--;
		}
		stock10yen -= change / 10;
	}

	public int getSale() {
		return sale;
	}

	public int get10YenStock() {
		return stock10yen;
	}

	public int get50YenStock() {
		return stock50yen;
	}

	public int get100YenStock() {
		return stock100yen;
	}

	public int get500YenStock() {
		return stock500yen;
	}
}

スレッド

スレッドは、並行処理を行う仕組み。
Javaでは言語仕様で並行処理をサポートしている。

Threadクラスを継承してクラスを作成する。そのクラスのインスタンスを生成し、start() メソッドを呼ぶとスレッドが起動される。スレッドが起動されると、run() メソッドが呼び出される。

Threadを使ったサンプル

MyThread.java

public class MyThread extends Thread {
	@Override
	public void run() {
		for (int i = 0; i < 5; i++) {
			System.out.println("Threadの実行中" + i);
		}
	}
}

MyThreadTest.java

public class MyThreadTest {

	public static void main(String[] args) {
		Thread t = new MyThread();
		t.start();
		for (int i = 0; i < 5; i++) {
			System.out.println("mainの実行中" + i);
		}
	}

}

Runnableインタフェースを使用する方法もある。

MyRunnable.java

public class MyRunnable implements Runnable {
	@Override
	public void run() {
		for (int i = 0; i < 5; i++) {
			System.out.println("Runnableの実行中" + i);
		}
	}
}

MyRunnableTest.java

public class MyRunnableTest {
	public static void main(String[] args) {
		Runnable r = new MyRunnable();
		Thread t = new Thread(r);
		t.start();
		for (int i = 0; i < 5; i++) {
			System.out.println("mainの実行中" + i);
		}
	}
}

スレッドのライフサイクル

スレッドのライフサイクルは重要!(試験問題に出す!)

SleepThread.java

public class SleepThread extends Thread {
	@Override
	public void run() {
		try {
			System.out.println(getName() + " sleep前");
			Thread.sleep(3000);
			System.out.println(getName() + " sleep後");
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
}

SleepThreadTest.java

public class SleepThreadTest {

	public static void main(String[] args) {
		SleepThread t = new SleepThread();
		t.start();
		System.out.println("mainのEND");
	}

}

複数のスレッドを同時に起動するサンプル。
SleepThreadTest2.java

public class SleepThreadTest2 {

	public static void main(String[] args) {
		for (int i = 0; i < 3; i++) {
			SleepThread t = new SleepThread();
			t.start();
		}
		System.out.println("mainのEND");
	}

}

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です