Learn Drools (Part 4): Inferences
Learn how you can set up inferences in Drools to create more modular rules that can be changed on the go.
Join the DZone community and get the full member experience.
Join For FreeAs per the dictionary, inference means, "An assumption or conclusion that is rationally and logically made, based on the given facts or circumstances."
An inference is based off of facts, so the reasoning for the conclusion is often logical.
Example: According to a technical interview, we can make an assumption that a candidate is good, bad, or moderate. The thing to notice here is that we don’t know how good the candidate is, as we do not work with them, but according the candidate's answers (facts), we can logically judge and come to a conclusion.
So, how do Drools' inferences work?
Let's try to understand it with a problem statement.
We want to give a laptop to those people who are in IT department whose designation is the manager.
When we get this problem statement, we will try to solve it with a Drools rule. Inputting the logic, we use Ifdepertment=="IT" and isManager=true, then we print "Give laptop."
As simple as that.
Solution
rule "give Laptop"
when
$dept: Department(name=="IT");
$emp: Employee(dept == $dept,manager==true);
then
$emp.setMessage("Give Laptop");
System.out.println($emp.getName()+ ": "+$emp.getDept().getName()+ ":"+$emp.getMessage());
end
Is this an elegant way to solve this problem?
The answer depends. If this IT Manager condition check (depertment=="IT" and isManager=true) is used in several rules, then it is not an elegant way to implement it. We can do it better, and here's the potential where we can use Inference.
On other hand, if this condition used only one or in a few rules and no future changes will be needed, then make it simple. The above rule is just fine, as I am a big follower of YAGNI (You aren't gonna need it).
Why is the above not a judicious way to define the rules where the same condition is used in several of them?
Let's say the IT manager condition is used in 100 different rules, and based on that condition, we will take different actions. So far, the condition has not changed, so our design is good. But think about if the condition requirements are changed — now the client wants (department=="IT" and isManager=true and experience>15). Now this experience factor is set to greater than 15, and that has to be taken into account.
So, we need to change the condition in 100 rules. A simple change is then hard to implement because the condition has been duplicated over 100 time.
It would be nice if could we design in a such a way that we can define the condition in one place and use that reference in other places — like soft link in Unix or environment variables in Windows.
By doing that , we can use the reference to the condition in every rule. We set JAVA_HOME="C;//java"; and use JAVA_HOME anywhere to declare a Java path, so in case we change the Java path to c://javaone, that will be reflected everywhere.
Fortunately, we can do the same thing in Drools via an Inference. Let's see how to do it step by step.
Step 1
Create a rule where we define the condition and create a reference to that in the "then" part, so we can use this reference in other rules.
rule "IT Manager Inference"
when
$dept: Department(name=="IT");
$emp: Employee(dept == $dept,manager==true);
then
insert(new ITManager($emp));
end
Please note the then part of the rule, where we insert a Java object called ITManager, which takes the employee reference. With this statement, we instruct Drools to insert an ITManager object into the KnowledgeFactory, which holds the "when" parts. It acts as a reference, like the JAVA_HOME key.
Step 2
Let's create other rules where we use this IT Manager reference.
rule "give Manager Laptop"
when
$emp: Employee();
$itManager: ITManager(emp == $emp);
then
$emp.setMessage("Give Laptop");
System.out.println($emp.getName()+ ": "+$emp.getDept().getName()+ ":"+$emp.getMessage());
end
Pay attention to the "when" part here. We check to see if the current employee is matching the ITManager condition. Here, Drools replaces ITManger with the actual "when" condition stated in the ITManager inference rule.
Step 3
Let's look at our complete Drools rule file.
package com.rules
import com.example.droolsExample.pojo.Employee
import com.example.droolsExample.pojo.Department
import com.example.droolsExample.pojo.ITManager
rule "IT Manager Inference"
when
$dept: Department(name=="IT");
$emp: Employee(dept == $dept,manager==true);
then
insert(new ITManager($emp));
end
rule "give Manager Laptop"
when
$emp: Employee();
$itManager: ITManager(emp == $emp);
then
$emp.setMessage("Give Laptop");
System.out.println($emp.getName()+ ": "+$emp.getDept().getName()+ ":"+$emp.getMessage());
end
This setup ensures any changes in we made will be automatically reflected in our "Give Laptop" rule.
Step 4: Java Objects
Employee.java
package com.example.droolsExample.pojo;
public class Employee {
String name;
boolean manager;
String message;
Department dept;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public boolean isManager() {
return manager;
}
public void setManager(boolean manager) {
this.manager = manager;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public Department getDept() {
return dept;
}
public void setDept(Department dept) {
this.dept = dept;
}
}
ITManager.java
package com.example.droolsExample.pojo;
public class ITManager {
private Employee emp;
public ITManager(Employee emp)
{
this.emp=emp;
}
public Employee getEmp() {
return emp;
}
public void setEmp(Employee emp) {
this.emp = emp;
}
}
Department.java
package com.example.droolsExample.pojo;
public class Department {
String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
DroolTest.java
package com.example.droolsExample;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import org.drools.compiler.compiler.DroolsParserException;
import org.drools.compiler.compiler.PackageBuilder;
import org.drools.core.RuleBase;
import org.drools.core.RuleBaseFactory;
import org.drools.core.WorkingMemory;
import com.example.droolsExample.pojo.Department;
import com.example.droolsExample.pojo.Employee;
public class DroolsTest {
public static void main(String[] args) throws DroolsParserException,
IOException {
DroolsTest droolsTest = new DroolsTest();
droolsTest.executeDroolsEmployee("/com/rules/employeeInfernce.drl");
}
public void executeDroolsEmployee(String ruleFile) throws DroolsParserException, IOException {
PackageBuilder packageBuilder = new PackageBuilder();
InputStream resourceAsStream = getClass().getResourceAsStream(ruleFile);
Reader reader = new InputStreamReader(resourceAsStream);
packageBuilder.addPackageFromDrl(reader);
org.drools.core.rule.Package rulesPackage = packageBuilder.getPackage();
RuleBase ruleBase = RuleBaseFactory.newRuleBase();
ruleBase.addPackage(rulesPackage);
WorkingMemory workingMemory = ruleBase.newStatefulSession();
Department dep = new Department();
dep.setName("Civil");
Department dep1 = new Department();
dep1.setName("IT");
Employee emp = new Employee();
emp.setName("Shamik Mitra");
emp.setManager(true);
emp.setDept(dep1);
Employee emp1 = new Employee();
emp1.setName("Samir Mitra");
emp1.setManager(true);
emp1.setDept(dep);
workingMemory.insert(dep);
workingMemory.insert(dep1);
workingMemory.insert(emp);
workingMemory.insert(emp1);
workingMemory.fireAllRules();
}
}
Step 5: Output
Shamik Mitra: IT:Give Laptop
And that's it! Congratulations, you've set up an inference.
Published at DZone with permission of Shamik Mitra, DZone MVB. See the original article here.
Opinions expressed by DZone contributors are their own.
Trending
-
Demystifying SPF Record Limitations
-
DevOps Midwest: A Community Event Full of DevSecOps Best Practices
-
Front-End: Cache Strategies You Should Know
-
Exploring the Capabilities of eBPF
Comments