FP in Scala. p4~11 Part1 関数型プログラミングの基礎 タグ管理
package org.ababup1192
case class Charge(creditCard: CreditCard, amount: Int) {
def combine(other: Charge): Charge = {
if (creditCard == other.creditCard) {
Charge(creditCard, amount + other.amount)
} else {
throw new Exception("Can't combine charges to different card")
}
}
}
trait Payments {
def charge(creditCard: CreditCard, price: Int): Unit
}
// 実際の決済を行わないモックを挟むことで、テストがしやすくなる。
object PaymentsMock extends Payments {
override def charge(creditCard: CreditCard, price: Int): Unit = {
println(s"CreditCard charge $price.")
}
}
class CreditCard {
override def toString: String = {
s"CreditCard"
}
}
package org.ababup1192
object CafeSimulator extends App {
val cafe = new Cafe
val creditCard = new CreditCard()
println("---- Single payment test. ----")
println(cafe.buyCoffee(creditCard))
println("---- Multiple payment test. ----")
println(cafe.buyCoffees(creditCard, 10))
// Payments traitを実装したオブジェクトは引き続き使える。
val (coffees, charge) = cafe.buyCoffees(creditCard, 10)
PaymentsMock.charge(charge.creditCard, charge.amount)
}
package org.ababup1192
class Coffee(val price: Int) {
// 補助コンストラクタ ↑基本コンストラクタを必ず呼び出す。
def this() = this(300)
override def toString: String = {
s"Coffee($price)"
}
}
class Cafe {
def buyCoffee(creditCard: CreditCard): (Coffee, Charge) = {
val cup = new Coffee()
// 決済処理はここでは行わず、コーヒーとChargeオブジェクトを返すだけ。副作用が無くなり、テストが非常に簡単に。
(cup, Charge(creditCard, cup.price))
}
// 決済をその場で行わない(副作用がない)buyCoffee関数の利用とChargeのcombine関数により一括決済が可能に。
def buyCoffees(creditCard: CreditCard, n: Int): (List[Coffee], Charge) = {
val purchases: List[(Coffee, Charge)] = List.fill(n)(buyCoffee(creditCard))
// List((1,"a"),(2,"b"),(3,"c"),(4,"d")).unzip
// res: (List[Int], List[String]) = (List(1, 2, 3, 4),List(a, b, c, d))
val (coffees, charges) = purchases.unzip
// Chargeのリストから1つずつ要素を取り出して行き、まとめて1つのChargeへ
(coffees, charges.reduce((c1, c2) => c1.combine(c2)))
}
// 無造作なChargeリストからクレジットカードごとにまとめChargeをまとめる。そうすることで手数料を節約できる。
// List(Charge(AliceのCard, 100), Charge(BobのCard, 100), Charge(AliceのCard, 200)) =>
// List(Charge(AliceのCard, 300), Charge(BobのCard, 100))
def coalesce(charges: List[Charge]): List[Charge] = {
charges.groupBy(_.creditCard).values.map(_.reduce(_ combine _)).toList
}
}