Accueil > Divers > Drools : Mise à jour de faits et boucles infinies

Drools : Mise à jour de faits et boucles infinies

Dans l’article précédent, nous avions fait nos premiers pas avec Drools, dans le cas d’une banque qui doit déterminer l’éligibilité à un prêt d’un client.

Nous avions écrit deux règles pour déterminer le nombre de pros et de cons d’un client. Mais une fois que nous avons ce nombre de pros et cons, il faut bien ajouter une règle pour dire si au final, le client est solvable ou pas. Rajoutons alors cette règle :

rule "a droit à un prêt"
    when
        personne: Personne( $numDossierPersonne:numDossier, pros > cons  )
    then
        personne.setSolvable(true);
        System.out.println("Le dossier "+ $numDossierPersonne+" est solvable !");
end

Et rajoutons dans notre main le code suivant pour vérifier si, après raisonnement, un client est solvable ou pas :

 

for(Personne p: personnes){
	System.out.println(" Dossier n°"+ p.getNumDossier()+ ": ");
	System.out.println("Solvabilité du client = " + p.isSolvable());
		}

Ca y est, tout est prêt ! Lançons maintenant notre petit programme.

Et là, déception. On voit dans la console :

Règle « a un CDI » déclenchée !

Règle « a un bon salaire » déclenchée !

Dossier n°1:

      -Pros = 3

      -Cons = 0

Dossier n°1:

Solvabilité du client = false

 

Notre client a un nombre de pros (3) supérieur à son nombre de cons (0) et pourtant, il n’est pas détecté comme solvable.

En fait, c’est tout à fait normal. Dans les règles « a un CDI » et « a un bon salaire », nous avons modifié des propriétés du fait « Personne » mais il faut que la mémoire de travail prenne en compte ce changement, et ce n’est pas automatique.

Pour cela, il faut donc mettre à jour notre fait dans la mémoire de travail avec la méthode update dans le RHS:

 

rule "a un CDI"
    when
        personne: Personne( $numDossierPersonne:numDossier  )
        c: Caracteristique( numDossier == $numDossierPersonne, nom == 'CDI' , valeur ==1  )
    then
        personne.setPros(personne.getPros()+1);
        System.out.println("Règle \"a un CDI\" déclenchée !");
   		update(personne);
end

Et là, attention ! Si on lance notre moteur de règles tout de suite, la console affichera la phrase suivante en boucle, sans jamais s’arrêter :

Règle « a un CDI » déclenchée !

Pourquoi ? Parce que lorsqu’on fait un update sur un fait, cela relance l’évaluation des règles et le raisonnement. Et comme notre condition est toujours vraie, notre règle sera toujours déclenchée, ce qui cause une boucle infinie.

Pour éviter cela, il faut ajouter un attribut no-loop dans notre règle :

rule "a un CDI"
no-loop
    when
…

Les attributs sont des caractéristiques d’une règle, à placer entre le nom de la règle et le when.

L’attribut no-loop signifie qu’une fois que la règle est déclenchée, elle ne pourra plus être déclenchée même si elle modifie un fait qui, normalement, devrait la relancer.

Après avoir rajouté ça, relançons notre moteur de règles. On obtient dans notre console :

Règle « a un CDI » déclenchée !

Règle « a un bon salaire » déclenchée !

Le dossier 1 est solvable !

Dossier n°1:

      -Pros = 3

      -Cons = 0

Dossier n°1:

Solvabilité du client = true

 

Magnifique, tout fonctionne.

A un détail près, le nombre de pros a été modifié par la règle « a un CDI » mais aussi par la règle « a un bon salaire ». Et cette dernière modification n’a pas été pris en compte dans la mémoire de travail.

Ici, ça ne pose pas de souci, parce que la règle « a un CDI » a également été déclenchée, ce qui fait que le nombre de pros de la personne en mémoire de travail a augmenté. Mais si on est amené à supprimer cette règle pour une quelconque raison, le client ne sera plus solvable.

Et oui, vous avez compris, il faut aussi ajouter un update dans la règle « a un bon salaire ». Et aussi un no-loop pour éviter la boucle infinie.

Super, lançons maintenant notre raisonnement.

Et là, encore une boucle infinie …

Règle « a un CDI » déclenchée !

Règle « a un bon salaire » déclenchée !

Règle « a un CDI » déclenchée !

Règle « a un bon salaire » déclenchée !

Drools a décidément quelques subtilités qu’il faut comprendre …pas très clair tout ça. Essayons de schématiser un peu, on a 2 règles, la règle A et B, contenant toutes les 2 un udpate sur un fait commun et l’attribut no-loop.

La règle A modifie un fait en mémoire de travail qui doit la déclencher à nouveau, mais comme elle a l’attribut no-loop, elle ne s’exécute pas à nouveau. Mais rien ne l’empêche de déclencher la règle B, qui elle va déclencher la règle A, etc … Nous avons donc bien une boucle infinie.

Pour éviter cela, Drools a mis en place un autre attribut : lock-on-active. Avec cet attribut mis à true, une règle déjà déclenchée ne pourra pas être exécutée une 2è fois au cours d’un même raisonnement.

Nous aurons donc les règles suivantes :

rule "a un CDI"
lock-on-active true
    when
        personne: Personne( $numDossierPersonne:numDossier  )
        c: Caracteristique( numDossier == $numDossierPersonne, nom == 'CDI' , valeur ==1  )
    then
        personne.setPros(personne.getPros()+1);
        System.out.println("Règle \"a un CDI\" déclenchée !");
   		update(personne);
end

rule "a un bon salaire"
lock-on-active true
    when
      personne: Personne( $numDossierPersonne:numDossier  )
      c: Caracteristique( numDossier == $numDossierPersonne, nom == 'Salaire' , valeur >=45000, valeur <60000  )
    then
        personne.setPros(personne.getPros()+2);
        System.out.println("Règle \"a un bon salaire\" déclenchée !");
     //   update(personne);
end

rule "a droit à un prêt"
    when
        personne: Personne( $numDossierPersonne:numDossier, pros > cons  )
    then
        personne.setSolvable(true);
        System.out.println("Le dossier "+ $numDossierPersonne+" est solvable !");
end

Après raisonnement, nous obtenons en console :

Règle « a un CDI » déclenchée !

Règle « a un bon salaire » déclenchée !

Le dossier 1 est solvable !

Dossier n°1:

      -Pros = 3

      -Cons = 0

Dossier n°1:

Solvabilité du client = true

 

Et là nous pouvons être sûrs d’avoir le bon résultat.

Pour terminer cette série d’articles, je vous invite à comprendre comment déclencher dans un ordre précis les règles dans un fichier via cet article.

Categories: Divers Tags:


un × 2 =