Let's start by adding support for adding a soda to our order:
- To do this, we'll define Soda as a sealed class with several options to choose from:
sealed class Soda(name: String) : Item(name)
object Coke : Soda("Coke")
object Sprite : Soda("Sprite")
object Dr_Pepper : Soda("Dr Pepper")
- With our Soda class in place, we can add a soda to our order by accessing Order.items directly:
val order = order {
this.items[Coke] = this.items.getOrDefault(Coke, 0) + 1
}
This works, but is verbose and requires direct interaction with the underlying data structure. Let's improve this by making the syntax more natural and by hiding the data structure.
There are a couple of ways in which we can do this, as follows:
- First, let's refactor the previous code into a simple method so that we can add a soda to the map:
fun soda(soda: Soda) = items.put(soda, items.getOrDefault(soda, 0) + 1)
- Now, we can add a soda with much less code and without leaking implementation details:
val order = order {
soda(Coke)
}
- We can also implement the unaryPlus operator for our Soda class to help provide a more fluent syntax for adding a soda to the order. We can do this by making use of the previous method's implementation:
operator fun Soda.unaryPlus() = soda(this)
- With this new add operator, we can add a soda to the order like this:
val order = order {
soda(Coke)
+Sprite
}
What if we wanted to add multiple sodas to our order? We could manually add the soda each time we wanted it, or we could provide a mechanism for updating the quantity at the same time as declaring the item.
To implement this, we'll add a new infix function to the Soda class:
infix fun Soda.quantity(quantity: Int) {
items.put(this, items.getOrDefault(this, 0) + quantity)
}
Now, we can add a soda and specify a quantity in a very human-readable way:
val order = order {
soda(Coke)
+Sprite
Coke quantity 2
}