import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.channels.ReceiveChannel
import kotlinx.coroutines.channels.SendChannel
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
enum class CoffeeType { ESPRESSO, LATTE }
class Milk
class GroundCoffee
sealed class Coffee
class Espresso(ground: GroundCoffee) : Coffee() {
override fun toString(): String = "Espresso"
}
class Latte(milk: Milk, espresso: Espresso) : Coffee() {
override fun toString(): String = "Latte"
}
suspend fun main(): Unit = coroutineScope {
val orders = Channel<CoffeeType>(Channel.UNLIMITED)
val coffees = Channel<Coffee>(Channel.UNLIMITED)
serveOrders(orders, coffees, "Alice")
serveOrders(orders, coffees, "Bob")
serveOrders(orders, coffees, "Celine")
serveOrders(orders, coffees, "Dave")
launch {
for (coffee in coffees) {
println("Coffee $coffee is ready")
}
}
println("Welcome to Dream Coffee!")
println("Press E to get espresso, L to get latte.")
while (true) {
val type = when (readlnOrNull()) {
"E" -> CoffeeType.ESPRESSO
"L" -> CoffeeType.LATTE
else -> continue
}
orders.send(type)
println("Order for $type sent")
}
}
fun CoroutineScope.serveOrders(
orders: ReceiveChannel<CoffeeType>,
servedOrders: SendChannel<Coffee>,
baristaName: String
) = launch {
for (order in orders) {
val coffee = makeCoffee(order, baristaName)
servedOrders.send(coffee)
}
}
private suspend fun makeCoffee(order: CoffeeType, baristaName: String): Coffee {
val groundCoffee = groundCoffee(baristaName)
val espresso = makeEspresso(groundCoffee, baristaName)
return when (order) {
CoffeeType.ESPRESSO -> espresso
CoffeeType.LATTE -> {
val milk = brewMilk(baristaName)
Latte(milk, espresso)
}
}
}
suspend fun groundCoffee(baristaName: String): GroundCoffee {
println("$baristaName: Grinding coffee...")
delay(3000)
return GroundCoffee()
}
suspend fun brewMilk(baristaName: String): Milk {
println("$baristaName: Brewing milk...")
delay(3000)
return Milk()
}
suspend fun makeEspresso(ground: GroundCoffee, baristaName: String): Espresso {
println("$baristaName: Making espresso...")
delay(3000)
return Espresso(ground)
}