SmartMonsters

Friday, December 13, 2013

Scalable Command Routing Via the Strategy and HashedAdaptor OO Design Patterns

TriadCity's command vocabulary is far larger than the 400 or so commands typed by players.  Every Admin and Builder action is implemented as a command — there are tens of thousands altogether.

In C, you'd probably route user input through a simple switch() statement. CircleMUD does this.  The parser and command router are tightly coupled inside one master controlling statement which switch()es on the user's first word of input. Logic can be parceled out to functions, or handled right there inside the switch() statement itself. This is fine for a couple of hundred commands, but growing it to tens of thousands would be a performance and maintenance disaster.

We ensure infinite command routing scalability for TriadCity by combining the Strategy and Hashed Adapter OO patterns.

Strategy encapsulates an algorithm as an object, allowing algorithms to be selected and substituted at runtime. In Java you'll typically implement Strategy via an Interface defining the algorithm's contract. Classes implementing that Interface define the available algorithms. In TriadCity we have a Command Interface defining our Strategy contract:

public Interface Command {
          public abstract boolean doCommand(<multile-args-go-here>);
}

And we have many thousands of classes implementing that Interface, for example, Chat, which implements the doCommand() method to iterate all NPCs and logged-in Players, sending the arguments to the chat channel.

The Hashed Adapter pattern achieves scalable routing by stashing these concrete Command instances in a HashMap keyed by command name. Each concrete Command is a Singleton; we populate the map with command names as keys, instantiated Command Singletons as values. The resulting routing algorithm is super-simple:

Command command = this.commands.get(commandName);
command.doCommand(<multiple-args-passed-here>);

There's other logic wrapped around these lines, for example authorization checking for security. But that's the idea.

By using Hashed Adapter this way, routing scales infinitely without performance degradation. Server processing is very efficient, and for developers there's no nightmare switch() statement of ten million lines to maintain. By using Strategy, it's very easy to do the file housekeeping necessary to keep tens of thousands of Command implementations easy to navigate.

No comments:

Post a Comment