Instantly share code, notes, and snippets.

Embed
What would you like to do?
自動販売機2.0をScalazのStateモナドを使って実装する http://devtesting.jp/tddbc/?TDDBC%E5%A4%A7%E9%98%AA3.0%2F%E8%AA%B2%E9%A1%8C
package org.nisshiee.vendingmachine
case class Juice(name: String, price: Int)
object Juice {
val coke = Juice("コーラ", 120)
val redbull = Juice("Red Bull", 200)
}
package org.nisshiee.vendingmachine
sealed trait Money
sealed trait UsableMoney extends Money
case object Money10 extends UsableMoney
case object Money50 extends UsableMoney
case object Money100 extends UsableMoney
case object Money500 extends UsableMoney
case object Money1000 extends UsableMoney
case object Money1 extends Money
case object Money5 extends Money
case object Money2000 extends Money
case object Money5000 extends Money
case object Money10000 extends Money
object Money {
implicit class RichMoney(val self: Money) extends AnyVal {
def value = self match {
case Money10 => 10
case Money50 => 50
case Money100 => 100
case Money500 => 500
case Money1000 => 1000
case Money1 => 1
case Money5 => 5
case Money2000 => 2000
case Money5000 => 5000
case Money10000 => 10000
}
}
}
package org.nisshiee.vendingmachine
import scalaz._, Scalaz._
case class VendingMachine(total: Int, stock: Map[Juice, Int], sellAmount: Int)
object VendingMachine {
lazy val init: VendingMachine = VendingMachine(0, Map(Juice.coke -> 5), 0)
val total = State[VendingMachine, Int] { vm => (vm, vm.total) }
val insert: UsableMoney => State[VendingMachine, Unit] = m => State {
vm => (vm.copy(total = vm.total + m.value), ())
}
val payback = State[VendingMachine, Int] { vm => (vm.copy(total = 0), vm.total) }
val stock: Juice => State[VendingMachine, Int] = j => State { vm =>
(vm, vm.stock.get(j) | 0)
}
val allStock = State[VendingMachine, Map[Juice, Int]] { vm => (vm, vm.stock) }
val checkPurchaseCondition: Juice => State[VendingMachine, Boolean] = j => for {
t <- total
st <- stock(j)
} yield t >= j.price && st > 0
val buy: Juice => State[VendingMachine, Boolean] = j => for {
p <- checkPurchaseCondition(j)
st <- stock(j)
t <- total
_ <- State[VendingMachine, Unit] { vm =>
if (p)
vm.copy(
total = t - j.price
,stock = vm.stock + ((j, st - 1))
,sellAmount = vm.sellAmount + j.price
) -> ()
else
vm -> ()
}
} yield (p)
val sellAmount = State[VendingMachine, Int] { vm => (vm, vm.sellAmount) }
}
package org.nisshiee.vendingmachine
import scalaz._, Scalaz._
import org.specs2._
import VendingMachine._
class VendingMachineSpec extends Specification { def is =
"total" ^
"初期状態で0が返る" ! e1^
p^
"insert money" ^
"10円を入れて投入金額合計を取得すると10が返る" ! e2^
"50円を入れて投入金額合計を取得すると50が返る" ! e3^
"100円を入れて投入金額合計を取得すると100が返る" ! e4^
"500円を入れて投入金額合計を取得すると500が返る" ! e5^
"1000円を入れて投入金額合計を取得すると1000が返る" ! e6^
"10円、100円の順に入れて投入金額合計を取得すると110が返る" ! e7^
"10円、10円の順に入れて投入金額合計を取得すると20が返る" ! e8^
"10円、100円、500円の順に入れて投入金額合計を取得すると610が返る" ! e9^
p^
"payback" ^
"100円を入れて払い戻しをすると100円が出てきて投入金額合計は0になる" ! e10^
"10円、100円を入れて払い戻しをすると110円が出てきて投入金額合計は0になる" ! e11^
p^
"stock" ^
"初期状態でコーラ120円の在庫が5個" ! e12^
"初期状態でRedBull200円の在庫が0個" ! e12_2^
p^
"allStock" ^
"初期状態でコーラ120円の在庫が5個" ! e13^
p^
"checkPurchaseCondition" ^
"初期状態で120円以上入れるとコーラを買える" ! e14^
"初期状態で100円しか入れてない状態ではコーラを買えない" ! e15^
"初期状態でRedBullはお金が足ないと買えない" ! e16^
"初期状態でRedBullはお金が足りてても買えない" ! e17^
p^
"buy" ^
"200円入れた状態でコーラ購入に成功し、コーラ在庫と投入金額が減る" ! e18^
"100円入れた状態でコーラ購入に失敗し、コーラ在庫も投入金額も変わらない" ! e19^
p^
"sellAmount" ^
"初期状態で0" ! e20^
"コーラ1個買うと120" ! e21^
"コーラ2個買うと240" ! e22^
end
def e1 = total eval VendingMachine.init must equalTo(0)
def e2 = (insert(Money10) >> total) eval VendingMachine.init must equalTo(10)
def e3 = (insert(Money50) >> total) eval VendingMachine.init must equalTo(50)
def e4 = (insert(Money100) >> total) eval VendingMachine.init must equalTo(100)
def e5 = (insert(Money500) >> total) eval VendingMachine.init must equalTo(500)
def e6 = (insert(Money1000) >> total) eval VendingMachine.init must equalTo(1000)
def e7 =
(insert(Money10) >> insert(Money100) >> total) eval VendingMachine.init must equalTo(110)
def e8 =
(insert(Money10) >> insert(Money10) >> total) eval VendingMachine.init must equalTo(20)
def e9 =
(insert(Money10) >> insert(Money100) >> insert(Money500) >> total) eval VendingMachine.init must equalTo(610)
def e10 = (for {
_ <- insert(Money100)
pb <- payback
t <- total
} yield (pb, t)) eval VendingMachine.init must equalTo((100, 0))
def e11 = (for {
_ <- insert(Money10)
_ <- insert(Money100)
pb <- payback
t <- total
} yield (pb, t)) eval VendingMachine.init must equalTo((110, 0))
def e12 = stock(Juice.coke) eval VendingMachine.init must equalTo(5)
def e12_2 = stock(Juice.redbull) eval VendingMachine.init must equalTo(0)
def e13 = allStock eval VendingMachine.init must equalTo(Map(Juice.coke -> 5))
def e14 = (for {
_ <- insert(Money100)
_ <- insert(Money100)
c <- checkPurchaseCondition(Juice.coke)
} yield c) eval VendingMachine.init must beTrue
def e15 = (for {
_ <- insert(Money100)
c <- checkPurchaseCondition(Juice.coke)
} yield c) eval VendingMachine.init must beFalse
def e16 = (for {
_ <- insert(Money100)
c <- checkPurchaseCondition(Juice.redbull)
} yield c) eval VendingMachine.init must beFalse
def e17 = (for {
_ <- insert(Money100)
_ <- insert(Money100)
c <- checkPurchaseCondition(Juice.redbull)
} yield c) eval VendingMachine.init must beFalse
def e18 = (for {
_ <- insert(Money100)
_ <- insert(Money100)
p <- buy(Juice.coke)
st <- stock(Juice.coke)
t <- total
} yield (p, st, t)) eval VendingMachine.init must equalTo((true, 4, 80))
def e19 = (for {
_ <- insert(Money100)
p <- buy(Juice.coke)
st <- stock(Juice.coke)
t <- total
} yield (p, st, t)) eval VendingMachine.init must equalTo((false, 5, 100))
def e20 = sellAmount eval VendingMachine.init must equalTo(0)
def e21 = (for {
_ <- insert(Money100)
_ <- insert(Money100)
_ <- buy(Juice.coke)
sa <- sellAmount
} yield sa) eval VendingMachine.init must equalTo(120)
def e22 = (for {
_ <- insert(Money100)
_ <- insert(Money100)
_ <- insert(Money100)
_ <- buy(Juice.coke)
_ <- buy(Juice.coke)
sa <- sellAmount
} yield sa) eval VendingMachine.init must equalTo(240)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment