Hello, While attempting to integrate Jess with some existing Java code, I have stumbled across what I think is a very useful design pattern. I want to share this pattern with you guys and get your feedback on it.
Let's consider the following graph of Java objects: +----------+ +----------------------+ | FuelLine | | Engine | -----> +----------+ +----------------------+ | pressure | +-------+ | running | +----------+ | Plane | -----> | requiredFuelPressure | +-------+ +----------------------+ +----------+ | start | | Ignition | | stop | -----> +----------+ +----------------------+ | running | +----------+ I want to pass this entire object graph into Jess and have rules which can match across all of these objects. I'll start by defclass'ing all of the classes involved: (defclass plane airport.Plane) (defclass engine airport.Engine) (defclass fuel-line airport.FuelLine) (defclass ignition airport.Ignition) Now I need to define rules to start and stop the engine: 1) The fuel-line must maintain a fuel pressure above a certain amount while the engine is in operation, or the engine will stall. 2) In order to start the engine, the fuel pressure must be over this limit and the ignition system must be engaged. We need to be able to traverse the whole object graph to implement each of these rules. However, the plane is the only object that we want definstance explicitly in our Java code. We could try to implement rule (1) by matching on (plane) and checking the rest of our conditions with (test) and (call), but this looks quite ugly: (defrule engine-ignition (plane (engine ?e)) (test (> (call (call ?e getFuelLine) getPressure) (call ?e getRequiredFuelPressure))) (test (call (call ?e getIgnition) isRunning)) (test (not (call ?e isRunning))) => (call ?e start)) I would much prefer to match each object as a separate fact: (defrule engine-ignition (plane (engine ?e)) (engine (OBJECT ?e) (ignition ?i) (fuelLine ?f) (requiredFuelPressure ?required) (running FALSE)) (ignition (OBJECT ?i) (running TRUE)) (fuel-line (OBJECT ?f) (pressure ?pressure)) (test (> ?pressure ?required)) => (call ?e start)) (defrule engine-stalls (plane (engine ?e)) (engine (OBJECT ?e) (fuelLine ?f) (requiredFuelPressure ?required) (running TRUE)) (fuel-line (OBJECT ?f) (pressure ?pressure)) (test (< ?pressure ?required)) => (call ?e stop)) In order for this to work, we need to use backward chaining so that each object is definstance'd when required. Not only do we need to turn on backwards-chaining for each class, but we need to add a rule that looks for need-* facts with an explicit value set in the OBJECT slot and then uses that value to definstance the object. (do-backwards-chaining engine) (defrule allow-navigation-for-engine (need-engine (OBJECT ?obj&~nil)) => (definstance ?obj)) Because this code needs to be repeated for each class, I wrote my own version of (defclass) which does it automatically: ;; Jess Object Graph Navigation ;; Usage: (jogn-defclass templateName javaClass static|dynamic) (deffunction jogn-defclass (?type ?c ?dynamic) (defclass ?type ?c) (do-backward-chaining ?type) (eval (str-cat "(defrule declare-" ?type " (need-" ?type " (OBJECT ?obj&~nil))" " => " " (definstance " ?type " ?obj " ?dynamic "))"))) Is this functionality that should simply be built into Jess? Is there ever a time when it would not be valid to automatically definstance an object when the OBJECT slot is explicitly requested? If not, is this a useful design pattern? Or am I missing an easier or more elegant way to navigate an object graph? Can anyone think of a clever way to extend this style of rule matching to support one-to-many associations (e.g., if a plane contained a list of engines) ? I haven't had much luck with this. Thanks, Don -------------------------------------------------------------------- To unsubscribe, send the words 'unsubscribe jess-users [EMAIL PROTECTED]' in the BODY of a message to [EMAIL PROTECTED], NOT to the list (use your own address!) List problems? Notify [EMAIL PROTECTED] --------------------------------------------------------------------