Last active
October 15, 2024 16:03
-
-
Save adavis/b9d0a7544ae032e99664c41936503460 to your computer and use it in GitHub Desktop.
Sample Kotlin DSL for making a pizza
This file contains 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
import kotlin.contracts.ExperimentalContracts | |
import kotlin.contracts.InvocationKind | |
import kotlin.contracts.contract | |
@Target(AnnotationTarget.CLASS, AnnotationTarget.TYPE) | |
@DslMarker | |
annotation class PizzaMarker | |
class Pizza( | |
val name: String, | |
val sauce: String, | |
val toppings: Toppings | |
) { | |
override fun toString() = | |
"Name: $name\nSauce: $sauce\nToppings: $toppings\n\n" | |
} | |
@PizzaMarker | |
class Toppings { | |
private val pizzaToppings: MutableList<String> = mutableListOf() | |
fun cheese(): Toppings { | |
pizzaToppings.add("Cheese") | |
return this | |
} | |
fun pepperoni(): Toppings { | |
pizzaToppings.add("Pepperoni") | |
return this | |
} | |
fun mushrooms(): Toppings { | |
pizzaToppings.add("Mushrooms") | |
return this | |
} | |
override fun toString() = pizzaToppings.joinToString() | |
} | |
@PizzaMarker | |
class PizzaBuilder { | |
var name: String = "" | |
var sauce: String = "" | |
var toppings: Toppings? = null | |
fun build(): Pizza { | |
if (toppings == null) throw IllegalArgumentException("Your pizza must have toppings") | |
return Pizza( | |
name = name, | |
sauce = sauce, | |
toppings = toppings!! | |
) | |
} | |
} | |
@OptIn(ExperimentalContracts::class) | |
fun makePizza(init: (@PizzaMarker PizzaBuilder).() -> Unit): Pizza { | |
contract { callsInPlace(init, InvocationKind.EXACTLY_ONCE) } | |
return PizzaBuilder().apply { init() }.build() | |
} | |
@OptIn(ExperimentalContracts::class) | |
fun PizzaBuilder.toppings(block: Toppings.() -> Toppings) { | |
contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) } | |
Toppings().apply { block() }.also { this.toppings = it } | |
} | |
fun main() { | |
val cheesePizza = makePizza { | |
name = "Cheese pizza" | |
sauce = "Marinara" | |
toppings { | |
cheese() | |
} | |
} | |
val pepperoniPizza = makePizza { | |
name = "Pepperoni pizza" | |
sauce = "Tomato" | |
toppings { | |
// toppings { // This violates the @PizzaMarker | |
// pepperoni() | |
// } | |
cheese() | |
pepperoni() | |
mushrooms() | |
} | |
// cheese() // This violates encapsulation | |
} | |
// val veggiePizza = makePizza { | |
// name = "Veggie pizza" | |
// sauce = "Tomato" | |
// | |
// // you have to have toppings | |
// } | |
println(cheesePizza) | |
println(pepperoniPizza) | |
// println(veggiePizza) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment