Waarom een Regelengine?
Voordat we beginnen met coderen, laten we eerst bespreken waarom je een regelengine zou willen gebruiken:
- Scheidt bedrijfslogica van de kernapplicatiecode
- Maakt het mogelijk voor niet-technische mensen om regels aan te passen zonder een volledige implementatie
- Maakt je systeem beter aanpasbaar aan veranderingen (en geloof me, veranderingen komen eraan)
- Verbetert onderhoudbaarheid en testbaarheid
Nu we allemaal op dezelfde golflengte zitten, laten we aan de slag gaan!
De Kerncomponenten
Onze regelengine zal bestaan uit drie hoofdonderdelen:
- Regel: De individuele bedrijfsregel
- RegelEngine: Het brein dat de regels verwerkt
- Feit: De data waarop onze regels zullen werken
Laten we deze één voor één bekijken.
1. De Regelinterface
Allereerst hebben we een interface nodig voor onze regels:
public interface Rule {
boolean evaluate(Fact fact);
void execute(Fact fact);
}
Eenvoudig, toch? Elke regel weet hoe hij zichzelf moet evalueren tegen een feit en wat te doen als het overeenkomt.
2. De Feitklasse
Vervolgens definiëren we onze Feitklasse:
public class Fact {
private Map attributes = new HashMap<>();
public void setAttribute(String name, Object value) {
attributes.put(name, value);
}
public Object getAttribute(String name) {
return attributes.get(name);
}
}
Deze flexibele structuur stelt ons in staat om elk type data aan onze feiten toe te voegen. Zie het als een sleutel-waarde opslag voor je bedrijfsdata.
3. De RegelEngine Klasse
Nu voor het hoofdonderdeel, onze RegelEngine:
public class RuleEngine {
private List rules = new ArrayList<>();
public void addRule(Rule rule) {
rules.add(rule);
}
public void process(Fact fact) {
for (Rule rule : rules) {
if (rule.evaluate(fact)) {
rule.execute(fact);
}
}
}
}
Hier gebeurt de magie. De engine doorloopt alle regels, evalueert en voert ze uit indien nodig.
Alles Samenbrengen
Nu we onze kerncomponenten hebben, laten we zien hoe ze samenwerken met een eenvoudig voorbeeld. Stel dat we een kortingssysteem bouwen voor een e-commerce platform.
public class DiscountRule implements Rule {
@Override
public boolean evaluate(Fact fact) {
Integer totalPurchases = (Integer) fact.getAttribute("totalPurchases");
return totalPurchases != null && totalPurchases > 1000;
}
@Override
public void execute(Fact fact) {
fact.setAttribute("discount", 10);
}
}
// Gebruik
RuleEngine engine = new RuleEngine();
engine.addRule(new DiscountRule());
Fact customerFact = new Fact();
customerFact.setAttribute("totalPurchases", 1500);
engine.process(customerFact);
System.out.println("Korting: " + customerFact.getAttribute("discount") + "%");
In dit voorbeeld, als een klant meer dan $1000 aan aankopen heeft gedaan, krijgt hij 10% korting. Eenvoudig, effectief en gemakkelijk aanpasbaar.
Verder Uitbreiden
Nu we de basis hebben, laten we enkele manieren verkennen om onze regelengine te verbeteren:
1. Regelprioriteiten
Voeg een prioriteitsveld toe aan regels en sorteer ze in de engine:
public interface Rule {
int getPriority();
// ... andere methoden
}
public class RuleEngine {
public void addRule(Rule rule) {
rules.add(rule);
rules.sort(Comparator.comparingInt(Rule::getPriority).reversed());
}
// ... andere methoden
}
2. Regelketening
Sta toe dat regels andere regels activeren:
public class RuleEngine {
public void process(Fact fact) {
boolean ruleExecuted;
do {
ruleExecuted = false;
for (Rule rule : rules) {
if (rule.evaluate(fact)) {
rule.execute(fact);
ruleExecuted = true;
}
}
} while (ruleExecuted);
}
}
3. Regelgroepen
Organiseer regels in groepen voor beter beheer:
public class RuleGroup implements Rule {
private List rules = new ArrayList<>();
public void addRule(Rule rule) {
rules.add(rule);
}
@Override
public boolean evaluate(Fact fact) {
return rules.stream().anyMatch(rule -> rule.evaluate(fact));
}
@Override
public void execute(Fact fact) {
rules.forEach(rule -> {
if (rule.evaluate(fact)) {
rule.execute(fact);
}
});
}
}
Prestatieoverwegingen
Hoewel onze regelengine behoorlijk soepel is, zijn er altijd manieren om te optimaliseren:
- Gebruik een efficiëntere datastructuur voor regelopslag (bijv. een boom voor hiërarchische regels)
- Implementeer caching voor vaak geraadpleegde feiten of regelbeoordelingen
- Overweeg parallelle verwerking voor grote regelsets
Onderhoudstips
Om te voorkomen dat je regelengine een onhandelbaar beest wordt:
- Documenteer elke regel grondig
- Implementeer versiebeheer voor je regels
- Creëer een gebruiksvriendelijke interface voor niet-technische gebruikers om regels te wijzigen
- Controleer en verwijder regelmatig verouderde regels
De Conclusie
En daar heb je het! Een slanke, efficiënte regelengine in ongeveer 150 regels code. Het is flexibel, uitbreidbaar en kan je misschien redden van de volgende bedrijfslogica-apocalyps. Onthoud, de sleutel tot een goede regelengine is het eenvoudig houden terwijl je complexiteit toestaat wanneer dat nodig is.
Voordat je gaat, hier is een gedachte om over na te denken: Hoe zou dit regelengine-concept kunnen worden toegepast op andere delen van je codebase? Zou het complexe besluitvormingsprocessen in je huidige project kunnen vereenvoudigen?
Veel programmeerplezier, en moge je bedrijfslogica altijd dynamisch geweldig zijn!
"Eenvoud is de ultieme verfijning." - Leonardo da Vinci (Hij had het niet over coderen, maar hij had het zeker kunnen doen)
P.S. Als je dieper in regelengines wilt duiken, bekijk dan enkele open-source projecten zoals Easy Rules of Drools. Ze kunnen je ideeën geven om je zelfgemaakte engine naar een hoger niveau te tillen!