Background
One of the great benefits with Firebase is the support for transparent failover. All you as a game developer have to do is to make sure that your game state is serializable.
The Problem
One common pattern that follows from this approach is that non-serializable classes (such as the Notifier or the Scheduler) tend to get passed around a whole lot, because they can’t be stored as instance variables. If you want to do Domain Driven Development, this is a bad pattern.
Half of the Solution
Storing those instances as “transient” is always an option, but only solves half of the problem. On deserialization, those values will be null and there’s no way to recreate them in readObject().
The Solution
Instead of referring to the Scheduler and Notifier directly, create a wrapper class which holds them as transient members. Then, pass that into all your collaborating classes. The parent class will hold a reference to this same wrapper and on each call from Firebase, it will inject the live versions of the Notifier and Scheduler and since all collaborating classes refer to the same instance of the wrapper, we’re now all set!
An Example
I find that a concrete example always makes things clearer. Here’s some code for a poker game. First, here’s the game state class.
public class Poker implements Serializable { private Wrapper wrapper = new Wrapper(); public void addPlayer() { Player player = new Player(wrapper); ... } public void handle(GameDataAction action, Table table) { injectCallbacks(table.getNotifier(), table.getScheduler()); ... } private void injectCallbacks(GameNotifier notifier, Scheduler scheduler) { wrapper.setNotifier(notifier); wrapper.setScheduler(scheduler); } }
And here’s the wrapper.
public class Wrapper implements Serializable { private transient GameNotifer notifier; private transient Scheduler scheduler; ... getters and setters ... }
Which means that from any any place in the code, we can do something like player.updateBalance(newBalance), which would look like:
public class Player { private final transient Wrapper wrapper; public Player(Wrapper wrapper) { this.wrapper = wrapper; } public void updateBalance(int newBalance) { balance = newBalance; notifier().notifyAllPlayers(createGameDataAction(newBalance)); } private Notifier notifier() { return wrapper.getNotifier(); } }
So, time for you to fire up your refactoring tool and put some candy in that wrapper!