Most organizations I've worked for have at some point built a complicated message driven dispatch system as part of their software. Typically this is accompanied by grandiose pronouncements about how this "software bus/object broker/message controller" will bring about a maintenance utopia because new handlers can by added "just by adding an entry to a table", that ISVs will be beating down the door to provide plugins/cartridges/random-marketing-buzzword for the system, that custom processing for clients can be done by plugging in additional modules, etc. In practice everything that these dispatch systems try to achieve (usually badly) can be done with less effort and more reliability using a few common patterns and Java's dynamic class loading mechanism.
Let's start with dynamic dispatch. This is a common C programming technique - a "dispatch table" of function pointers, indexed by message type, is used to select the appropriate function to execute to process the message. A similar Java implementation would use a HashMap or array of classes with each class implementing an interface
that provided a common handleMessage
method. Of course your architects wouldn't be worth their Rational Rose licenses if they designed something this simple, so instead you'll probably get a complicated reflection-based scheme executing arbitrary class methods based on the closing value of the NASDAQ-100 index, the phases of the moon, and so on. You'll also get a steady stream of run-time failures as any errors in the dispatch table (misspelled class names, incorrect methods, etc.) aren't detected until the corresponding message is processed.
There's a simpler and more reliable alternative - the plain old Factory pattern. Your factory class has a newObject
method that consists of a big switch
or nested if
statement (we want the code to reference the actual classes rather than strings containing class names) that constructs and returns the appropriate message handler class. Simple, fast (reflection usually kills JIT), and robust - make a mistake in a class definition and the code doesn't compile.
"But we can't dynamically change the system - we have to modify the code and rebuild to change behavior". Take a good look at your production environment - you're already doing this if your system is of any importance. Nobody just drops in new salary calculation code into the corporate payroll system - it goes through QA, gets change controlled, and is then installed in a scheduled maintenance window (unless it's a fix for a high-priority bug).
You can use the meta-factory pattern for more flexibility without any loss of reliability. A meta-factory provides a static method that returns a factory object which is used to construct new objects. Typically the meta-factory checks if a custom factory class has been specified (by a system property or the like) and returns an new instance of the custom factory if this is the case, otherwise returning an instance of itself. If you really can't rebuild the entire system you need only provide a new factory class and any associated handler classes and restart the system. You can also use this approach to add custom handlers for specific messsages. In this case the custom factory subclasses the standard factory, with it's newObject
method returning a custom handler for the messages you've selected and calling super.newObject
for the others. This also lets you implement "software bus" designs that dispatch a message to multiple handlers by writing a wrapper handler class that invokes each message handler in turn - but unlike a software bus a utility class gives you an easy way to handle issues like whether you continue or abort executing handlers after one fails (the kind of thing your architects won't discover until the system is in production), and you can do so on a case-by-case basis. Code is more flexible than frameworks.