Pourquoi un moteur de règles ?

Avant de commencer à coder, parlons de pourquoi vous pourriez vouloir un moteur de règles :

  • Sépare la logique métier du code principal de l'application
  • Permet aux non-techniciens de modifier les règles sans déploiement complet
  • Rend votre système plus adaptable aux changements (et croyez-moi, les changements arrivent)
  • Améliore la maintenabilité et la testabilité

Maintenant que nous sommes tous sur la même longueur d'onde, passons à la pratique !

Les composants principaux

Notre moteur de règles se composera de trois parties principales :

  1. Règle : La règle métier individuelle
  2. MoteurDeRègles : Le cerveau qui traite les règles
  3. Fait : Les données sur lesquelles nos règles vont opérer

Analysons-les une par une.

1. L'interface de la règle

Tout d'abord, nous avons besoin d'une interface pour nos règles :


public interface Rule {
    boolean evaluate(Fact fact);
    void execute(Fact fact);
}

Simple, non ? Chaque règle sait comment s'évaluer par rapport à un fait et quoi faire si elle correspond.

2. La classe Fact

Ensuite, définissons notre classe Fact :


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);
    }
}

Cette structure flexible nous permet d'ajouter tout type de données à nos faits. Pensez-y comme à un magasin de données clé-valeur pour vos données métier.

3. La classe RuleEngine

Maintenant, pour l'attraction principale, notre RuleEngine :


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);
            }
        }
    }
}

C'est là que la magie opère. Le moteur parcourt toutes les règles, les évalue et les exécute si nécessaire.

Tout assembler

Maintenant que nous avons nos composants principaux, voyons comment ils fonctionnent ensemble avec un exemple simple. Disons que nous construisons un système de réduction pour une plateforme de commerce électronique.


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);
    }
}

// Utilisation
RuleEngine engine = new RuleEngine();
engine.addRule(new DiscountRule());

Fact customerFact = new Fact();
customerFact.setAttribute("totalPurchases", 1500);

engine.process(customerFact);

System.out.println("Discount: " + customerFact.getAttribute("discount") + "%");

Dans cet exemple, si un client a effectué plus de 1000 $ d'achats, il obtient une réduction de 10 %. Simple, efficace et facilement modifiable.

Aller plus loin

Maintenant que nous avons les bases, explorons quelques façons d'améliorer notre moteur de règles :

1. Priorités des règles

Ajoutez un champ de priorité aux règles et triez-les dans le moteur :


public interface Rule {
    int getPriority();
    // ... autres méthodes
}

public class RuleEngine {
    public void addRule(Rule rule) {
        rules.add(rule);
        rules.sort(Comparator.comparingInt(Rule::getPriority).reversed());
    }
    // ... autres méthodes
}

2. Chaînage de règles

Permettre aux règles de déclencher d'autres règles :


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. Groupes de règles

Organisez les règles en groupes pour une meilleure gestion :


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);
            }
        });
    }
}

Considérations de performance

Bien que notre moteur de règles soit assez efficace, il existe toujours des moyens de l'optimiser :

  • Utilisez une structure de données plus efficace pour le stockage des règles (par exemple, un arbre pour les règles hiérarchiques)
  • Mettez en cache les faits ou évaluations de règles fréquemment consultés
  • Envisagez le traitement parallèle pour de grands ensembles de règles

Conseils de maintenabilité

Pour éviter que votre moteur de règles ne devienne ingérable :

  • Documentez chaque règle de manière approfondie
  • Mettez en œuvre un contrôle de version pour vos règles
  • Créez une interface conviviale pour que les utilisateurs non techniques puissent modifier les règles
  • Auditez régulièrement et nettoyez les règles obsolètes

À retenir

Et voilà ! Un moteur de règles simple et efficace en environ 150 lignes de code. Il est flexible, extensible et pourrait bien vous sauver du prochain cataclysme de logique métier. Rappelez-vous, la clé d'un bon moteur de règles est de le garder simple tout en permettant la complexité lorsque nécessaire.

Avant de partir, voici une réflexion : comment ce concept de moteur de règles pourrait-il s'appliquer à d'autres parties de votre code ? Pourrait-il simplifier les processus de prise de décision complexes dans votre projet actuel ?

Bon codage, et que votre logique métier soit toujours dynamiquement géniale !

"La simplicité est la sophistication ultime." - Léonard de Vinci (Il ne parlait pas de codage, mais il aurait pu)

P.S. Si vous souhaitez approfondir les moteurs de règles, consultez des projets open-source comme Easy Rules ou Drools. Ils pourraient vous donner des idées pour amener votre moteur maison au niveau supérieur !