Possibility and Probability

A Python programmer with a personality thinking about space exploration

10 May 2007

Business Rules and the Visitor Pattern

by Nick

One thing I really love about Python is that you can really knock things out quickly with it. I’ve been thinking about business rules lately, and specifically about how to make them cleaner with respect to your code. When it comes to rules, there are several places and ways you can encode them in your application, each with its own pro’s and con’s. Lately I’ve been reading a lot about Lisp based languages and every now and then I’ll read a reference to using Lisp code not only as a configuration file, but as a “executable” that carries its own data. The more I thought about this, the more I wondered if this type of separation was possible in Java. Sure, I could just code the rules in Java, but where’s the fun in that? Via its reflect package, Java can do some pretty neat tricks, so I decided to try and code up some ideas in Python first since its so concise (and can also do reflection, etc.). Basically a case of “anything you can do, I can do better”. A while ago I read a really interesting article about the visitor pattern. Most of the time this pattern is explained in terms of something like a driver: You have some kind of hardware that needs to call a driver to setup its connection to the system. Using the accept() and visit() methods you can make a class that can register itself with the caller and allow it to configure itself, the end result being that the caller doesn’t need to know (i.e. be linked with) the details of the visitor. It occurred to me that this is the same situation with externalized business logic: The rule engine is reading in an external file and then based on the contents of that file, executing code. If a “rule” implemented the visitor pattern, and the “rule engine” implemented the accept(visitor) method, then this would allow the external file to dictate what the program would be doing without the program needing massive changes to alter the rules. It boils down to this: defining the core actions separately from the rules allows a better separation between  the two. The visitor pattern allows the two to be bound together. The end result is cleaner (smaller) code, that should be more “testable”. At least that’s my theory. I’ve put the python code implementing this up on my project page (here is the code). Its a very simple example, there is one rule per line, and the first token (space delimited) is the function to call followed by its arguments. Internally the RuleEngine class is calling Python’s eval() function and that is what actually executes the function from the data.txt file. For the function to execute successfully it needs to exist (i.e. it needs to be defined in the file, or be a built-in Python function). Note that this is not the safest approach, it will allow malicious code to execute. A more secure way to do this would be to set up a dict (or, if this is done in Java a HashMap) that contains a mapping between the rule tokens and the function to be executed. That way the execution of the RuleEngine can be controlled somewhat, in that it will only allow certain functions to be executed.

tags: