Hi, I was looking for a way to handle business rules according to a char. I.e. if I receive char 'x' fire Business rule 1, if I receive char 'y' fire Business rule 2, etc. I thought that an 'if' statement would be hard to maintain if new rules came along, and I thought reflection to generate a rule object based on the char might be overkill. Is there a way of mapping business rules to a simple char without making a hard to maintain 'if' statement or expensive reflection?
Intuition tells me there's a factory pattern solution somewhere, but I can't work it out for sure! Anyone have any ideas? Thanks, James
The next primitive step up is a case statement. It's an honorable tool; even when they grow, at worst they're tedious to read. You could pass each character as an event object, rather than a primitive, and treat your business rules as event listeners. You can arrange it so that the business rule 1 object is only interested in 'a' events, etc., etc. And there are some more complex approaches, such as a combination of Interpreter and Command design patterns (see this book) which would make sense if you anticipated a large-scale association here.
Make visible what, without you, might perhaps never have been seen. - Robert Bresson
Here's something I've done on more than one occassion and had very good success. Personally, I think it is a good OO approach and it definitely avoids using an ugly "if/else" structure. 1. Create a "RuleHandler" interface or abstract class that defines a method to handle the rule processing, e.g. process(), handle(), validate() or whatever. Most likely, the method will have to take as a parameter some kind of object that encapsulates the data that needs to be validated. Also, you may want to throw a custom "RuleViolationException" that can describe what went wrong. 2. Then create a class that implements/extends RuleHandler for each business rule you have. Each subclass will provide the specific implementation of handling a business rule. 3. So how do you dynamically use the right RuleHandler depending on your char? Create the mapping in a properties file. I prefer XML but you can also use a standard properties (my.key=value). For example the XML props file would look like this: ... <RuleHandler> <!-- The ID (char) for your rule --> <RuleID>x</RuleID> <!-- A class that implements/extends RuleHandler --> <RuleHandlerClass>XRule</RuleHandlerClass> </RuleHandler> <RuleHandler> <RuleID>y</RuleID> <RuleHandlerClass>YRule</RuleHandlerClass> </RuleHandler> ... 4. Then dynamically getting an instance of your rule handler and processing the rule is easy:
Of course, I'm open to any criticism on this approach. Good luck!
Blake Minghelli<br />SCWCD<br /> <br />"I'd put a quote here but I'm a non-conformist"
You can convert the run-time reflection into initialisation-time reflection by using a Map which maps handlerNames to RuleHandler instances. That should address any performance concerns you might have. In the original problem, the approach can be simpler and faster still, as you can probably convert the char to an integer index into a RuleHandler array. The simplest approach would be to use a properties configuration file of the formEtcetera. This can be loaded up in a java.util.Properties object. At initialisation time,Will initialise the array of business rule handlers (the character 'A' corresponding to the zeroth element). - Peter [ January 06, 2003: Message edited by: Peter den Haan ]
I had thought of a hashtable too, but never having used the approach myself I didn't mention it. Have you used this approach, Peter? I've been getting wary myself of using properties for this kind of work, since it a) implies a start/restart routine to bring in changes, and b) leaves a proper restart in the hands of correct editing. My recent work with a lot of Sun ONE products in particular makes me wonder if XML-everything for config files is such a cool idea.
Joined: Sep 13, 2002
I've been getting wary myself of using properties for this kind of work, since it a) implies a start/restart routine to bring in changes, and b) leaves a proper restart in the hands of correct editing. My recent work with a lot of Sun ONE products in particular makes me wonder if XML-everything for config files is such a cool idea.
Storing the RuleHandlers in a map still requires that the map is populated somehow, most likely during initialization, so that by itself does not get you away from needing a restart to load new RuleHandlers. If you don't want to restart in order to change handlers then you could consider replacing the properties lookup with a db lookup, or implement an "auto-refreshing" properties object. However, that would still leave you with your concern about improper editing... I guess that depends on your deployment methodology. We always deploy to a test environment first, that way if there are any config errors (e.g. spelling error in a properties file or a db record), they'll be caught before moving to production. Using XML for a config file is a matter of preference. At first I didn't like the idea, but the more I learned about it, it became very comfortable for me. We still use a lot of regular props files too! Bottom line - make it as configurable as possible and stay away from hard-coding your rule handling. That's my opinion for what it's worth!
Start/restart is really only a problem for long running processes such as servlets. Assuming that this is part of some batch process, you don't have to worry about reloading the handler array. If it is part of a servlet/jsp application then reloading is an issue. Make the handler array static (it should probably be anyway) and then write a servlet to run a reload method against the handler array. Whenever you create a new rule or update an old rule, you run the reload servlet and your handler array is up to date.
Originally posted by Blake Minghelli: If you don't want to restart in order to change handlers then you could consider [...] an "auto-refreshing" properties object. However, that would still leave you with your concern about improper editing...
For our in-house controller, we implemented an auto-refresh by checking the configuration file timestamp every X seconds. This will read the new file and parse it into a new Configuration object. If an exception is thrown during the parsing and validation phase, this is logged and the new Configuration is discarded, leaving the old, working, configuration in its place. Only when the new configuration is valid it is actually installed. This solves not only the restart problem, but also the editing problem. Not if you're editing the config file while the system is down, of course, but if you've taken the system down for maintenance anyway the editing problem isn't much of a problem in the first place. You could do the same thing here. Just replace the old handler array only if you could successfully create the new one, including all object instantiations. - Peter [ January 06, 2003: Message edited by: Peter den Haan ]
Originally posted by James Bowen: I thought that an 'if' statement would be hard to maintain if new rules came along,
Do you feel the same about the switch-statement proposed above? If so, why? I think for rather static situations (rules don't come along very often and it would be okay to change the source code in that cases), the switch solution would be quite ok. If your requirements are more dynamic, you will need one of the more complex solutions, of course.
The soul is dyed the color of its thoughts. Think only on those things that are in line with your principles and can bear the light of day. The content of your character is your choice. Day by day, what you do is who you become. Your integrity is your destiny - it is the light that guides your way. - Heraclitus
subject: Avoid clumsy 'if' statements but avoid reflection too.