Shopping Basket
To convert the Shopping Basket system from Akka typed actors to untyped actors, we need to modify the code so that it aligns with the untyped actor model in Akka. Below is the step-by-step process to achieve this.
Step 1: Define Messages and Data Models
The message and data model definitions remain mostly the same, but we'll omit the sealed trait for commands, as untyped actors don't enforce the use of sealed trait in the same way.
// Messages
case class AddItem(item: Item)
case object Checkout
// Data Models
case class Item(name: String, price: BigDecimal)
case class Receipt(items: List[Item], total: BigDecimal)
Step 2: Define the BasketActor
Now, let's define the BasketActor. In untyped actors, we extend the Actor trait and implement the receive method. The actor's state can be managed as mutable variables inside the actor.
import akka.actor.{Actor, ActorLogging, Props}
class BasketActor extends Actor with ActorLogging {
var items: List[Item] = List.empty
override def receive: Receive = {
case AddItem(item) =>
log.info(s"Item added to basket: ${item.name}")
items = items :+ item
case Checkout =>
val total = items.map(_.price).sum
log.info(s"Checking out with total: $total")
// In a real application, you might want to send the receipt to another actor or persist it.
context.stop(self)
}
}
object BasketActor {
def props(): Props = Props(new BasketActor)
}
Step 3: Set Up the Actor System and Test
Finally, let's set up the ActorSystem, create the BasketActor, and send messages to test the functionality. The main difference in untyped actors is that we use system.actorOf to create actors and ! (tell) to send messages.
import akka.actor.{ActorSystem, Props}
object ShoppingApp extends App {
// Create Actor System
val system = ActorSystem("shoppingSystem")
// Create BasketActor
val basketActor = system.actorOf(BasketActor.props(), "basketActor")
// Interact with the BasketActor
basketActor ! AddItem(Item("Apple", BigDecimal("0.60")))
basketActor ! AddItem(Item("Banana", BigDecimal("0.40")))
basketActor ! Checkout
// Shutdown the actor system after a short delay to see the output.
Thread.sleep(1000) // Not ideal for real applications, just for demonstration
system.terminate()
}
Explanation:
- Actor Creation: In untyped actors,
BasketActoris created usingsystem.actorOf(BasketActor.props(), "basketActor"). - Message Handling: The
receivemethod handles incoming messages by pattern matching on the message type. - State Management: State (i.e., the list of
items) is maintained as a mutable variable inside the actor. - Logging: The
ActorLoggingtrait is mixed in to provide easy access to logging within the actor.