I'm new to Akka and I'm exited about this technology, but I can't understand how to cook actors without business logic. Let's say I need to make a game. The game has limits on players and after choosing winner it can't be chosen more.
public class Game {
private List<String> participants;
private int maxParticipants;
private String winner;
private boolean ended;
public int getMaxParticipants() {
return maxParticipants;
}
public int getParticipantsSize() {
return participants.size();
}
public void addParticipant(String participant) {
participants.add(participant);
}
public boolean isEnded() {
return ended;
}
public void chooseWinner() {
if (ended) {
// do some stuff
}
winner = participants.get(randomNumber);
ended = true;
}
}
And actor class:
public class GameActor extends UntypedActor {
private Game game = new Game();
@Override
public void onReceive(Object message) throws Exception {
if (message instanceof AddParticipant) {
// For example there can't be more then two participants.
// I want to have this "if" inside my Game class, not here
if (game.getParticipantsSize() >= game.getMaxParticipants()) {
// do something about it
}
game.addParticipant(message.getParticipant());
}else if (message instanceof ChooseWinner){
// We can't choose winner after game ended. I don't want to have this logic here
game.chooseWinner();
}
}
}
Right now I see several approaches. In simple cases they all could work, but they are very limited:
Rise exceptions. Works only in negative cases. If everything is OK I don't know what to do next. Also catch blocks is ugly and I don't want to maintain GameFullException, GameEndedException and so on.
Return some value. Like boolean in addParticipant if it was successful. Either limited usage or another layer of ifelse for return value.
- Game class can rise events and my actor can be subscribed to it.
Like this:
public class Game {
private List<Listener> listeners = new ArrayList<>();
public void addListener(Listener listener) {
listeners.add(listener);
}
public void riseEvent(Event event) {
listeners.forEach(l->l.handle(event));
}
}
And single listener is the actor:
public class GameActor extends UntypedActor {
private Game game = new Game();
@Override
public void onReceive(Object message) throws Exception {
if (message instanceof AddParticipant) {
game.addParticipant(message.getParticipant());
}else if (message instanceof ChooseWinner){
game.chooseWinner();
}else if( message instanceof Event){
// do something with actor state if needed
}
}
public void handle(Event event) {
self().tell(event, self());
}
}
The third one seems the most interesting and powerful for me, but it just seems wrong that my model here has one subscriber and it's an actor which in turn sends raised by model events to itself. Of course this game class is just an illustration(and I'm not sure if it's a good illustration of my problem) and such simple logic could be done in actor and it's OK, but I'm interested in principle of how to decouple business logic from actors, because I don't think actors are good place for business logic for many reasons. I'm interested both in Scala and Java. Some examples would be great.