6月8日

投稿者: | 2020年6月8日

「釣り銭がないときは購入できないようにする」テストに対して、VendingMachine側の実装を作っていく。

まず釣り銭を出せるかどうかをチェックするメソッドを用意する。
それをcanBuy()で呼び出すようにする。

	public boolean canBuy(String name) {
		List<Juice> list = juices.get(name);
		if (list.size() == 0) return false;
		Juice j = list.get(0);
		if (total < j.getPrice()) return false;
		// 釣り銭を考慮する
		int r = total - j.getPrice();
		// 釣り銭を用意できるか?
		if (!isRefundable(r)) return false;
		return true;
	}

	private boolean isRefundable(int r) {
		int y500 = y500coins;
		int y100 = y100coins;
		int y50 = y50coins;
		int y10 = y10coins;
		while (r >= 500 && y500 > 0) {
			y500--;
			r -= 500;
		}
		while (r >= 100 && y100 > 0) {
			y100--;
			r -= 100;
		}
		while (r >= 50 && y50 > 0) {
			y50--;
			r -= 50;
		}
		while (r >= 10 && y10 > 0) {
			y10--;
			r -= 10;
		}
		if (r > 0) return false;
		return true;
	}

全体の動作をわかりやすくするために、メッセージ出力を追加してみる。

package jp.abc;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;


/**
 * 自動販売機クラス
 * @author satoshis
 *
 */
public class VendingMachine {

	private int total = 0;
	private Map<String, List<Juice>> juices = new HashMap<>();
	private List<Juice> colas = new ArrayList<>();
	private List<Juice> waters = new ArrayList<>();
	private List<Juice> redbulls = new ArrayList<>();
	private int y500coins = 10;
	private int y100coins = 10;
	private int y50coins = 10;
	private int y10coins = 10;

	public VendingMachine() {
		for (int i = 0; i < 5; i++) {
			colas.add(new Juice("コーラ", 120));
			waters.add(new Juice("水", 100));
			redbulls.add(new Juice("レッドブル", 200));
		}
		juices.put("コーラ", colas);
		juices.put("水", waters);
		juices.put("レッドブル", redbulls);
	}

	/**
	 * 10円玉、50円玉、100円玉、500円玉、1000円札だけ扱える
	 * @param money
	 */
	public void put(int money) {
		if (money == 10 || money == 50 || money == 100 || money == 500 || money == 1000) {
			total += money;
			if (money == 10) y10coins++;
			if (money == 50) y50coins++;
			if (money == 100) y100coins++;
			if (money == 500) y500coins++;
		}
	}

	public int entered() {
		return total;
	}

	public int refund() {
		int r = total;
		while (total > 0) {
			while (total >= 500 && y500coins > 0) {
				y500coins--;
				total -= 500;
			}
			while (total >= 100 && y100coins > 0) {
				y100coins--;
				System.out.println("100円玉残り:" + y100coins);
				total -= 100;
			}
			while (total >= 50 && y50coins > 0) {
				y50coins--;
				System.out.println("50円玉残り:" + y50coins);
				total -= 50;
			}
			while (total >= 10 && y10coins > 0) {
				y10coins--;
				System.out.println("10円玉残り:" + y10coins);
				total -= 10;
			}
		}
		return r;
	}

	public List<Juice> getJuiceList() {
		List<Juice> list = new ArrayList<>();
		list.add(new Juice("コーラ", 120));
		list.add(new Juice("レッドブル", 200));
		list.add(new Juice("水", 100));
		return list;
	}

	public boolean isStock(String name) {
		return true;
	}

	public boolean canBuy(String name) {
		List<Juice> list = juices.get(name);
		// 在庫がなければfalse
		if (list.size() == 0) return false;
		Juice j = list.get(0);
		// 金額が足りないならfalse
		if (total < j.getPrice()) return false;
		// 釣り銭を考慮する
		int r = total - j.getPrice();
		// 釣り銭を用意できるか?
		if (!isRefundable(r)) return false;
		return true;
	}

	private boolean isRefundable(int r) {
		System.out.println(r);
		int y500 = y500coins;
		int y100 = y100coins;
		int y50 = y50coins;
		int y10 = y10coins;
		while (r >= 500 && y500 > 0) {
			y500--;
			r -= 500;
		}
		while (r >= 100 && y100 > 0) {
			y100--;
			System.out.println("y100:" + y100);
			r -= 100;
		}
		while (r >= 50 && y50 > 0) {
			y50--;
			System.out.println("y50:" + y50);
			r -= 50;
		}
		while (r >= 10 && y10 > 0) {
			y10--;
			System.out.println("y10:" + y10);
			r -= 10;
		}
		if (r > 0) {
			System.out.println(r);
			return false;
		}
		return true;
	}

	public Juice buy(String name) {
		List<Juice> list = juices.get(name);
		Juice j = list.remove(0);
		total -= j.getPrice();
		return j;
	}

	public Juice getJuice(String name) {
		List<Juice> list = juices.get(name);
		return list.get(0);
	}

}

VendingMachineTest

	@Test
	void 釣り銭がないときは購入できないようにする() {
		VendingMachine vm = new VendingMachine();
		// 1本目の水を購入
		System.out.println("1本目の水を購入");
		vm.put(500);
		boolean b = vm.canBuy("水");
		assertEquals(true, b);
		Juice j = vm.buy("水");
		assertEquals("水", j.getName());
		int m = vm.refund();
		assertEquals(400, m);
		// 100円玉残り6個
		// 2本目の水を購入
		System.out.println("2本目の水を購入");
		vm.put(500);
		b = vm.canBuy("水");
		vm.buy("水");
		m = vm.refund();
		assertEquals(400, m);
		// 100円玉残り2個
		// 3本目の水を購入
		System.out.println("3本目の水を購入");
		vm.put(500);
		b = vm.canBuy("水");
		vm.buy("水");
		m = vm.refund();
		assertEquals(400, m);
		// 100円玉残り0個、50円玉残り6個、10円玉残り10個
		// 4本目の水を購入
		System.out.println("4本目の水を購入");
		vm.put(500);
		b = vm.canBuy("水");
		vm.buy("水");
		m = vm.refund();
		assertEquals(400, m);
		// 100円玉残り0個、50円玉残り0個、10円玉残り0個
		// 5本目の水を購入?
		System.out.println("5本目の水を購入");
		vm.put(500);
		b = vm.canBuy("水");
		assertEquals(false, b);
	}

釣り銭がないときの動作を確認するためのテストを追加する。

	@Test
	void 釣り銭がないときは購入できないようにする() {
		VendingMachine vm = new VendingMachine();
		// 1本目の水を購入
		System.out.println("1本目の水を購入");
		vm.put(500);
		boolean b = vm.canBuy("水");
		assertEquals(true, b);
		Juice j = vm.buy("水");
		assertEquals("水", j.getName());
		int m = vm.refund();
		assertEquals(400, m);
		// 100円玉残り6個
		// 2本目の水を購入
		System.out.println("2本目の水を購入");
		vm.put(500);
		b = vm.canBuy("水");
		vm.buy("水");
		m = vm.refund();
		assertEquals(400, m);
		// 100円玉残り2個
		// 3本目の水を購入
		System.out.println("3本目の水を購入");
		vm.put(500);
		b = vm.canBuy("水");
		vm.buy("水");
		m = vm.refund();
		assertEquals(400, m);
		// 100円玉残り0個、50円玉残り6個、10円玉残り10個
		// 4本目の水を購入
		System.out.println("4本目の水を購入");
		vm.put(500);
		b = vm.canBuy("水");
		vm.buy("水");
		m = vm.refund();
		assertEquals(400, m);
		// 100円玉残り0個、50円玉残り0個、10円玉残り0個
		// 5本目の水を購入?
		System.out.println("5本目の水を購入");
		vm.put(500);
		b = vm.canBuy("水");
		assertEquals(false, b);
		m = vm.refund();
		assertEquals(500, m);
		// 100円だけ入れて水を買ってみる
		vm.put(100);
		b = vm.canBuy("水");
		assertEquals(true, b);
		vm.buy("水");
		m = vm.refund();
		assertEquals(0, m);
		// コーラを買ってみる
		vm.put(100);
		vm.put(100);
		b = vm.canBuy("コーラ");
		assertEquals(false, b);
		vm.put(10);
		vm.put(10);
		b = vm.canBuy("コーラ");
		assertEquals(true, b);
		vm.buy("コーラ");
		m = vm.refund();
		assertEquals(100, m);
	}

TDDはここまでで終わり。

次はWebアプリケーションを作っていく。

SpringBootプロジェクト

新しいプロジェクトを作成する。

[ファイル]-[新規]-[プロジェクト]を選択する。
新規プロジェクトのウィザードで「Spring スターター・プロジェクト」を選択して「次へ」
プロジェクト名が「demo」になっているので、そのままで「次へ」
「新規Springスターター・プロジェクト依存関係」で「Web」の下の「Spring Web」を選択して「完了」

パッケージエクスプローラーに「demo」プロジェクトが作られる。

demoプロジェクトは、Mavenというビルドツールを使うように設定してある。

最初にプロジェクト名[demo]を右クリックして[Maven]-[プロジェクトの更新]を選択する。
次にプロジェクト名[demo]を右クリックして[実行]-[Maven install]を選択する。
最後に「BUILD SUCCESS」と表示されればOK。

Webアプリケーションを起動するには「demo」を右クリックして[実行]-[Spring Boot アプリケーション]を選択する。
コンソールに「Started DemoApplication」と表示されれば起動されている。

プラウザで http://localhost:8080/ にアクセスすると「Whitelabel Error Page」と表示される。→Webアプリケーションが起動している。
コンソールタブの赤い■をクリックしてWebアプリケーションを終了する。

Hello worldを作ってみる。
src/main/java を右クリックして[新規]-[クラス]を選択する。
クラス名に「HelloController」を入力して「完了」する。

以下のコードを入力し、「demo」を右クリックして[実行]-[Spring Boot アプリケーション]を選択する。
コンソールに「Started DemoApplication」と表示されれば起動されている。

package com.example.demo;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class HelloController {
	@RequestMapping("/hello")
	public String index() {
		return "Hello world!";
	}
}

http://localhost:8080/ にアクセスすると「Hello world」と表示する。

HTMLを表示するために、thymeleafを追加する。

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.3.0.RELEASE</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.example</groupId>
	<artifactId>demo</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>demo</name>
	<description>Demo project for Spring Boot</description>

	<properties>
		<java.version>1.8</java.version>
	</properties>

	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
			<exclusions>
				<exclusion>
					<groupId>org.junit.vintage</groupId>
					<artifactId>junit-vintage-engine</artifactId>
				</exclusion>
			</exclusions>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-thymeleaf</artifactId>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>

</project>

src/main/java を右クリックして[新規]-[クラス]を選択する。
クラス名に「IndexController」を入力して「完了」する。
以下のコードを入力する。

package com.example.demo;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
public class IndexController {
	@RequestMapping("/")
	public String index() {
		return "index";
	}
}

src/main/resouces の中にある templates を右クリックして[新規]-[その他]を選択。
「HTMLファイル」を選択して「次へ」をクリック。
ファイル名を「index.html」にして「完了」する。

以下のコードを入力して、「demo」を右クリックして[実行]-[Spring Boot アプリケーション]を選択してWebアプリケーションを起動する。

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>index.html</title>
</head>
<body>
<h1>Hello spring boot</h1>
</body>
</html>

ブラウザで「http://localhost:8080/」にアクセスして「Hello spring boot」と表示されればOK。