Skip to content

Instantly share code, notes, and snippets.

@xaethos
Last active June 23, 2016 03:57
Show Gist options
  • Save xaethos/7b8ff8a55ea0680f8d65b2ffbbdfcccb to your computer and use it in GitHub Desktop.
Save xaethos/7b8ff8a55ea0680f8d65b2ffbbdfcccb to your computer and use it in GitHub Desktop.
Injectable PureMVC commands
package com.example;
import android.os.Bundle;
import android.support.annotation.VisibleForTesting;
import org.puremvc.java.interfaces.INotification;
import org.puremvc.java.patterns.command.SimpleCommand;
import org.puremvc.java.patterns.facade.Facade;
import javax.inject.Inject;
import javax.inject.Singleton;
@Singleton
public class FooCommand extends SimpleCommand {
/*
We'll call the commands via methods, so these don't need to be public.
*/
@VisibleForTesting static final String CMD_BAR = "FooCommand.Bar";
@VisibleForTesting static final String CMD_BAZ = "FooCommand.Baz";
/*
Same as above.
*/
@VisibleForTesting
static class BarParams {
public final String s;
public final int n;
private BarParams(String s, int n) {
this.s = s;
this.n = n;
}
}
/*
The command knows how to register itself with the facade.
No more keeping lists of command strings in two different classes.
*/
@Inject
public void register(Facade facade) {
facade.registerCommand(CMD_BAR, this);
facade.registerCommand(CMD_BAZ, this);
}
/*
Now executing a command becomes as simple as getting a reference to the
singleton instance and calling `fooCmd.bar("answer", 42)`
*/
public void bar(String s, int n) {
/*
Of course, we still send notifications behind the scenes.
*/
sendNotification(CMD_BAR, new BarParams(s, n));
}
public void baz(boolean b, float... fs) {
/*
How the command packages its parameters becomes an internal
implementation detail.
*/
Bundle params = new Bundle();
params.putBoolean("bool", b);
params.putFloatArray("floats", fs);
sendNotification(CMD_BAR, params);
}
@Override
public void execute(INotification notification) {
switch (notification.getName()) {
case CMD_BAR:
doBar((BarParams) notification.getBody());
break;
case CMD_BAZ:
doBaz((Bundle) notification.getBody());
break;
}
}
private void doBar(BarParams params) { /* ... */ }
private void doBaz(Bundle params) { /* ... */ }
}
/* Executing commands becomes much neater */
class Something {
/*
We can inject this since we're using Dagger.
Providing a `FooCommand.getInstance()` or `facade.getFooCommand()`
would be very easy, too.
*/
@Inject FooCommand fooCmd;
/*
Testing that a command is executed with correct parameters
becomes much easier: `verify(mockFooCmd).baz(true, 1.0, 3.4, 0.0)`
*/
public void doTestableStuff(float f) {
fooCmd.baz(true, f, 3.4f, 0.0f);
}
}
@benjamintanweihao
Copy link

benjamintanweihao commented Jun 23, 2016

I like this!

  • Dagger will ensure that each Command is a singleton
  • Dagger will automatically run register() after creating each command. No more figuring out "why doesn't the command run???"
  • Calling sendNotification(...) inside of methods of commands instead of calling sendNotification gives us better type safety. We won't be able to do something crazy like sendNotification(CommandFoo.NAME, new CommandBar.Params(...)).

The only thing I'll add is a default case in the execute method, to maybe throw and exception when an unexpected notification is received and having SimpleCommand be abstract and expect that register be implemented for "real" Commands.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment