ステップ5 釣り銭と売り上げ管理
お題の最後のステップは、次のようになっています。
ステップ5 釣り銭と売り上げ管理
- ジュース値段以上の投入金額が投入されている条件下で購入操作を行うと、釣り銭(投入金額とジュース値段の差分)を出力する。
- ジュースと投入金額が同じ場合、つまり、釣り銭0円の場合も、釣り銭0円と出力する。
- 釣り銭の硬貨の種類は考慮しなくてよい。
各パーツはだいたいできあがってきたので、ここでは自動販売機クラスに組み立てましょう。なお、題意を少し変えて、購入後に返却操作をしたときにお釣りが返ってくるものとします。
自動販売機クラスの設計
ここまで各バーツの設計を進めてきて、自動販売機クラスが備えるべき機能や状態はだいたい分かってきました。まとめると次の表のようになるでしょう。
自動販売機クラスが公開すべき状態と機能
| 利用者 | 種類 | シグネチャ |
| サービスマン | 状態 |
int Get在庫数(ジュース) int 売り上げ金額 |
| 機能 | void 在庫を追加する(ジュース, int) | |
| お客 | 状態 |
int 預り金 IEnumerable<ジュース> 購入品トレイ int 釣り銭トレイ |
| 機能 |
void お金を投入する(int) void 購入する(ジュース) void 返金してもらう() |
自動販売機クラスを作る
最初は、クラスを作ってみるテストです。Get在庫数()も一緒に実装してしまいましょう
[TestCase]
public void ConstructorTest() {
// 初期状態でコーラ5本を在庫している
var vendingMachine = new 自動販売機();
Assert.AreEqual(5, vendingMachine.Get在庫数(ジュース.コーラ));
}
自動販売機クラス
public class 自動販売機 {
internal 商品ラック _rack;
public 自動販売機() {
this._rack = new 商品ラック();
}
public int Get在庫数(ジュース kind) {
return _rack.Get在庫数(kind);
}
}
自動販売機クラスを完成させる
自動販売機クラスは、各パーツに処理を委譲してしまいますので、実装らしい実装はほとんどありません。そこで、前述のメソッドやプロパティをひととおり使用するシナリオを1つ書いて、残りを一気に実装してしまいましょう。
[TestCase]
public void シナリオTest() {
// 在庫を追加
var vm = new 自動販売機();
vm.在庫を追加する(ジュース.レッドブル, 5);
vm.在庫を追加する(ジュース.水, 5);
// 購入
Assert.AreEqual(0, vm.預り金);
CollectionAssert.IsEmpty(vm.購入品トレイ);
Assert.AreEqual(0, vm.釣り銭トレイ);
vm.お金を投入する(100);
vm.お金を投入する(100);
vm.お金を投入する(10);
vm.お金を投入する(10);
vm.お金を投入する(10);
vm.お金を投入する(1);
vm.お金を投入する(5);
Assert.AreEqual(230, vm.預り金);
Assert.AreEqual(6, vm.釣り銭トレイ);
vm.購入する(ジュース.コーラ);
Assert.AreEqual(110, vm.預り金);
CollectionAssert.AreEqual(
new List<ジュース>() { ジュース.コーラ, },
vm.購入品トレイ
);
Assert.AreEqual(6, vm.釣り銭トレイ);
vm.購入する(ジュース.レッドブル); //残金110円では買えない
Assert.AreEqual(110, vm.預り金);
CollectionAssert.AreEqual(
new List<ジュース>() { ジュース.コーラ, },
vm.購入品トレイ
);
Assert.AreEqual(6, vm.釣り銭トレイ);
vm.購入する(ジュース.水);
Assert.AreEqual(10, vm.預り金);
CollectionAssert.AreEqual(
new List<ジュース>() { ジュース.コーラ, ジュース.水 },
vm.購入品トレイ
);
Assert.AreEqual(6, vm.釣り銭トレイ);
vm.返金してもらう();
Assert.AreEqual(0, vm.預り金);
CollectionAssert.AreEqual(
new List<ジュース>() { ジュース.コーラ, ジュース.水 },
vm.購入品トレイ
);
Assert.AreEqual(16, vm.釣り銭トレイ);
// 販売後の状態
Assert.AreEqual(4, vm.Get在庫数(ジュース.コーラ));
Assert.AreEqual(5, vm.Get在庫数(ジュース.レッドブル));
Assert.AreEqual(4, vm.Get在庫数(ジュース.水));
Assert.AreEqual(220, vm.売り上げ金額);
}
自動販売機クラス
public class 自動販売機 {
public IList<ジュース> 購入品トレイ { get; private set; }
public int 釣り銭トレイ { get; private set; }
public int 預り金 { get { return _coinMech.預り金; } }
public int 売り上げ金額 { get { return _coinMech.売り上げ金額; } }
private コインメック _coinMech;
private 商品ラック _rack;
private コントローラー _controller;
public 自動販売機() {
this._rack = new 商品ラック();
this._coinMech = new コインメック();
this._controller = new コントローラー(_coinMech, _rack);
this.購入品トレイ = new List<ジュース>();
}
public int Get在庫数(ジュース kind) {
return _rack.Get在庫数(kind);
}
public void 在庫を追加する(ジュース kind, int number) {
_rack.在庫を追加する(kind, number);
}
public void お金を投入する(int coin) {
釣り銭トレイ += _coinMech.お金を投入する(coin);
}
public void 購入する(ジュース kind) {
ジュース commodity = _controller.購入する(kind);
if( !(commodity == null)) {
購入品トレイ.Add(commodity);
}
// ここで、預り金と最安商品価格とを比較して、返金してもらう()を呼び出すこともできる。
}
public void 返金してもらう() {
釣り銭トレイ += _coinMech.お金を払い戻す();
}
}
