6月4日

投稿者: | 2020年6月4日

自動販売機クラスをリファクタリングする

コーラの在庫をコレクションで管理するようにリファクタリングする。
修正箇所は以下の部分。
修正後、テストを実行したらグリーンになるのが確認できる。
テストがグリーンになったら、VendingMachine.javaを右クリックし、[チーム]-[コミット]を選択してコミットメッセージを入力し、コミットする。

package jp.abc;

import java.util.ArrayList;
import java.util.List;


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

	private int total = 0;
	private int stockWater = 5;
	private int stockRedbull = 5;
	private List<Juice> colas = new ArrayList<>();

	public VendingMachine() {
		for (int i = 0; i < 5; i++) {
			colas.add(new Juice("コーラ", 120));
		}
	}

	/**
	 * 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;
		}
	}

	public int entered() {
		return total;
	}

	public int refund() {
		int r = total;
		total = 0;
		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) {
		if (name.equals("水")) {
			if (stockWater == 0) return false;
			if (total >= 100) return true;
			return false;
		}
		if (name.equals("レッドブル")) {
			if (stockRedbull == 0) return false;
			if (total >= 200) return true;
			return false;
		}
		// 在庫切れはfalseを返す
		if (colas.size() == 0) return false;
		if (total >= 120) return true;
		return false;
	}

	public Juice buy(String name) {
		if (name.equals("水")) {
			total -= 100;
			stockWater--;
			return new Juice("水", 100);
		}
		if (name.equals("レッドブル")) {
			total -= 200;
			stockRedbull--;
			return new Juice("レッドブル", 200);
		}
		total -= 120;
		Juice j = colas.remove(0);
		return j;
	}

}

コーラと同様に、水についても在庫をコレクションで管理するようにリファクタリングする。

package jp.abc;

import java.util.ArrayList;
import java.util.List;


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

	private int total = 0;
	private int stockRedbull = 5;
	private List<Juice> colas = new ArrayList<>();
	private List<Juice> waters = new ArrayList<>();

	public VendingMachine() {
		for (int i = 0; i < 5; i++) {
			colas.add(new Juice("コーラ", 120));
			waters.add(new Juice("水", 100));
		}
	}

	/**
	 * 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;
		}
	}

	public int entered() {
		return total;
	}

	public int refund() {
		int r = total;
		total = 0;
		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) {
		if (name.equals("水")) {
			if (waters.size() == 0) return false;
			if (total >= 100) return true;
			return false;
		}
		if (name.equals("レッドブル")) {
			if (stockRedbull == 0) return false;
			if (total >= 200) return true;
			return false;
		}
		// 在庫切れはfalseを返す
		if (colas.size() == 0) return false;
		if (total >= 120) return true;
		return false;
	}

	public Juice buy(String name) {
		if (name.equals("水")) {
			total -= 100;
			Juice j = waters.remove(0);
			return j;
		}
		if (name.equals("レッドブル")) {
			total -= 200;
			stockRedbull--;
			return new Juice("レッドブル", 200);
		}
		total -= 120;
		Juice j = colas.remove(0);
		return j;
	}

}

コーラと同様に、レッドブルについても在庫をコレクションで管理するようにリファクタリングする。

package jp.abc;

import java.util.ArrayList;
import java.util.List;


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

	private int total = 0;
	private List<Juice> colas = new ArrayList<>();
	private List<Juice> waters = new ArrayList<>();
	private List<Juice> redbulls = new ArrayList<>();

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

	/**
	 * 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;
		}
	}

	public int entered() {
		return total;
	}

	public int refund() {
		int r = total;
		total = 0;
		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) {
		if (name.equals("水")) {
			if (waters.size() == 0) return false;
			if (total >= 100) return true;
			return false;
		}
		if (name.equals("レッドブル")) {
			if (redbulls.size() == 0) return false;
			if (total >= 200) return true;
			return false;
		}
		// 在庫切れはfalseを返す
		if (colas.size() == 0) return false;
		if (total >= 120) return true;
		return false;
	}

	public Juice buy(String name) {
		if (name.equals("水")) {
			total -= 100;
			Juice j = waters.remove(0);
			return j;
		}
		if (name.equals("レッドブル")) {
			total -= 200;
			Juice j = redbulls.remove(0);
			return j;
		}
		total -= 120;
		Juice j = colas.remove(0);
		return j;
	}

}

名前をキーにしてジュースを取得する処理が共通しているっぽいので、名前でジュースを取得できるテストを作成する。

VendingMachineTest.java

	@Test
	void 名前が一致するジュースを取得する() {
		VendingMachine vm = new VendingMachine();
		Juice j = vm.getJuice("コーラ");
		assertEquals("コーラ", j.getName());
	}

VendingMachineにgetJuice()をクイックフィックスで生成する。
コンパイルエラーが解消するのでテストを実行してエラーになるのを確認する。

	public Juice getJuice(String name) {
		return null;
	}

エラーになるのを確認できたら、fakeコードを書いてテストにパスさせる。

	public Juice getJuice(String name) {
		return new Juice("コーラ", 120);
	}

水とレッドブルのテストを追加する。

	@Test
	void 名前が一致するジュースを取得する() {
		VendingMachine vm = new VendingMachine();
		Juice j = vm.getJuice("コーラ");
		assertEquals("コーラ", j.getName());
		j = vm.getJuice("水");
		assertEquals("水", j.getName());
		j = vm.getJuice("レッドブル");
		assertEquals("レッドブル", j.getName());
	}

Mapを使って名前をキーにしてListを取得できるようにリファクタリングする。

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<>();

	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;
		}
	}

	public int entered() {
		return total;
	}

	public int refund() {
		int r = total;
		total = 0;
		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) {
		if (name.equals("水")) {
			if (waters.size() == 0) return false;
			if (total >= 100) return true;
			return false;
		}
		if (name.equals("レッドブル")) {
			if (redbulls.size() == 0) return false;
			if (total >= 200) return true;
			return false;
		}
		// 在庫切れはfalseを返す
		if (colas.size() == 0) return false;
		if (total >= 120) return true;
		return false;
	}

	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);
	}

}

canBuy()メソッドの中も、Mapを使ってListを取得するようにリファクタリングした。

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<>();

	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;
		}
	}

	public int entered() {
		return total;
	}

	public int refund() {
		int r = total;
		total = 0;
		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);
		if (list.size() == 0) return false;
		Juice j = list.get(0);
		return total >= j.getPrice();
	}

	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);
	}

}

ステップ6 釣り銭ストックの導入

自動販売機には、500円、100円、50円、10円がそれぞれ10枚ずつ釣り銭ようにストックされている。
500円玉を投入して水を購入する操作を繰り返すと、
1本目は釣り銭で100円玉4枚を使う。→100円玉残り6個
2本目は釣り銭で100円玉4枚を使う。→100円玉残り2個
3本目は釣り銭で100円玉2枚と50円玉4枚を使う。→100円玉残り2個、50円玉残り6個
4本目は釣り銭で10円玉10枚と50円玉6枚を使う。→100円玉残り0個、50円玉残り0個、10円玉残り0個
5本目は釣り銭不足(50円玉6枚と10円玉10枚で)で購入できない

	@Test
	void 釣り銭がないときは購入できないようにする() {
		VendingMachine vm = new VendingMachine();
		// 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本目の水を購入
		vm.put(500);
		vm.buy("水");
		m = vm.refund();
		assertEquals(400, m);
		// 100円玉残り2個
		// 3本目の水を購入
		vm.put(500);
		vm.buy("水");
		m = vm.refund();
		assertEquals(400, m);
		// 100円玉残り0個、50円玉残り6個、10円玉残り10個
		// 4本目の水を購入
		vm.put(500);
		vm.buy("水");
		m = vm.refund();
		assertEquals(400, m);
		// 100円玉残り0個、50円玉残り0個、10円玉残り0個
		// 5本目の水を購入?
		vm.put(500);
		b = vm.canBuy("水");
		assertEquals(false, b);
	}