Created
June 11, 2020 17:30
-
-
Save colomboe/595313e2c346d58a6d8212b2debe20b7 to your computer and use it in GitHub Desktop.
Videostore Kata declarative rules
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package videostore.declarative | |
import java.math.BigDecimal | |
import kotlin.reflect.KProperty | |
data class Days(val count: Int) | |
fun Int.days() = Days(this) | |
fun Int.day() = Days(this) | |
// ------------------------------------------------------------------------------------ | |
// DSL for rental cost | |
data class Money(val value: BigDecimal) | |
fun Double.dollars(): Money = Money(BigDecimal.valueOf(this)) | |
operator fun Money.plus(that: Money): Money = TODO() | |
typealias CostRule = (Days) -> Money | |
infix fun Money.`for the first`(days: Days): CostRule = { d -> TODO() } | |
infix fun Money.`daily, starting from`(days: Days): CostRule = { d -> TODO() } | |
@JvmName("andForDaysRule") | |
operator fun CostRule.plus(that: CostRule): CostRule = { TODO() } | |
// ------------------------------------------------------------------------------------ | |
// DSL for frequent renter points | |
data class Points(val points: Int) | |
fun Int.point() = Points(this) | |
operator fun Points.plus(that: Points): Points = TODO() | |
typealias PointsRule = (Days) -> Points | |
infix fun Points.`for renting at least`(days: Days): PointsRule = { d -> TODO() } | |
@JvmName("andForPointsRule") | |
operator fun PointsRule.plus(that: PointsRule): PointsRule = { TODO() } | |
// ------------------------------------------------------------------------------------ | |
// Domain implementation | |
sealed class MovieType | |
object Regular : MovieType() | |
object NewRelease : MovieType() | |
object Children : MovieType() | |
data class MovieTypeBusinessRules(val type: MovieType, | |
val rate: (Days) -> Money, | |
val frequentRenterPoints: (Days) -> Points) | |
private typealias movieType = MovieTypeBusinessRules | |
val businessRules = listOf( | |
movieType( | |
type = Regular, | |
rate = (2.00.dollars() `for the first` 2.days()) + (1.50.dollars() `daily, starting from` 3.days()), | |
frequentRenterPoints = 1.point() `for renting at least` 1.day() | |
), | |
movieType( | |
type = NewRelease, | |
rate = 3.00.dollars() `daily, starting from` 1.day(), | |
frequentRenterPoints = (1.point() `for renting at least` 1.day()) + (1.point() `for renting at least` 2.days()) | |
), | |
movieType( | |
type = Children, | |
rate = (1.50.dollars() `for the first` 3.days()) + (1.50.dollars() `daily, starting from` 3.days()), | |
frequentRenterPoints = 1.point() `for renting at least` 1.day() | |
) | |
) | |
// ------------------------------------------------------------------------------------ | |
// Usage | |
fun rulesForType(t: MovieType) = businessRules.first { it.type == t } | |
data class Rent(val movieType: MovieType, val days: Days) | |
fun <T> computeTotal(ruleProperty: KProperty<(Days) -> T>, sum: (T, T) -> T): (List<Rent>) -> T = { rents -> | |
rents.map { (type, days) -> ruleProperty.call(rulesForType(type))(days) }.reduce(sum) | |
} | |
val totalCost: (List<Rent>) -> Money = computeTotal(MovieTypeBusinessRules::rate, Money::plus) | |
val totalPoints: (List<Rent>) -> Points = computeTotal(MovieTypeBusinessRules::frequentRenterPoints, Points::plus) | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment