web-dev-qa-db-fra.com

créer un moteur de règles simple en Java

J'explore différentes manières de créer un moteur de règles métier simple en Java. Je dois présenter au client une simple application Web lui permettant de configurer un ensemble de règles. Un exemple de base de règles pourrait ressembler à ceci:

Voici un exemple:

 IF (PATIENT_TYPE = "A" AND ADMISSION_TYPE="O")
 SEND TO OUTPATIENT
 ELSE IF PATIENT_TYPE = "B" 
 SEND TO INPATIENT

Le moteur de règles est assez simple, l'action finale pourrait être une action sur deux, l'envoi aux patients hospitalisés ou ambulatoires. Les opérateurs impliqués dans une expression peuvent être =,>,<,!= et les opérateurs logiques entre les expressions sont AND, OR and NOT

Je souhaite créer une application Web permettant à l'utilisateur d'écrire un petit script dans une variable textarea. J'évaluerais l'expression. De cette façon, les règles commerciales sont expliquées en anglais simple et l'utilisateur professionnel contrôle totalement la logique.

D'après les recherches que j'ai effectuées jusqu'à présent, j'ai découvert ANTLR et écrit mon propre langage de script en tant qu'options possibles pour résoudre ce problème. Je n'ai pas exploré les options telles que le moteur de règles de Drools, car j'ai le sentiment que cela pourrait être exagéré ici. Avez-vous eu une expérience dans la résolution de ce genre de problèmes? Si oui, comment vous y êtes-vous pris? 

28
Jay

Je suggère d'utiliser quelque chose comme Drools . Créer votre propre solution personnalisée constituerait une perte de temps, car vous auriez à la déboguer tout en fournissant des fonctionnalités certainement inférieures à celles fournies par un moteur de règles comme Drools. Je comprends que Drools a une courbe d’apprentissage, mais je ne la comparerais pas à la création d’un langage personnalisé, ou d’une solution personnalisée ...

À mon avis, pour qu'un utilisateur puisse écrire des règles, il devrait apprendre quelque chose. Bien que je suppose que vous puissiez fournir un langage plus simple que le langage de la règle , vous ne captureriez jamais tous ses besoins. Le langage de règles de Drools serait assez simple pour des règles simples. De plus, vous pouvez lui fournir une documentation bien formée. Si vous envisagez de contrôler les règles créées par l'utilisateur final et appliquées sur le système, il serait peut-être plus sage de créer une interface graphique qui formerait les règles appliquées sur les baves.

J'espère que j'ai aidé!

10
Pantelis Natsiavas

Si vous recherchez quelque chose de plus léger que bave mais avec des fonctionnalités similaires, vous pouvez vérifier http://smartparam.org/ project Il permet de stocker des paramètres dans des fichiers de propriétés ainsi que dans des bases de données.

5
Jakub Kubrynski

Vous vous préparez à l'échec pour deux raisons principales:

  1. L'analyse du texte libre de l'utilisateur est HARD.
  2. Ecrire des parseurs en Java est un peu fastidieux

La résolution 1. va soit vous pousser dans le domaine flou de la PNL, pour lequel vous pouvez utiliser un outil comme OpenNLP ou quelque chose de cet écosystème. En raison des nombreuses manières subtiles dont l'utilisateur peut écrire, votre pensée sera biaisée en faveur d'une grammaire plus formelle. Faire ce travail vous aboutira dans une solution de type DSL, ou vous devrez concevoir votre propre langage de programmation.

J'ai obtenu des résultats raisonnables en utilisant des combinateurs d'analyseurs Scala pour analyser à la fois le langage naturel et des grammaires plus formelles. Les problèmes sont les mêmes, mais le code que vous devez écrire pour les résoudre est plus lisible.

En bout de ligne, même si vous songez à un langage de règles très simple, vous allez sous-estimer le nombre de scénarios que vous devez tester. NeilA a raison de vous conseiller de réduire la complexité en créant une interface utilisateur appropriée pour chaque type de règle. N'essayez pas d'être trop générique, sinon cela vous exploserait au visage.

5
iwein

D'après l'expérience passée, la solution basée sur des règles en "texte brut" est une très mauvaise idée, elle laisse beaucoup de place à l'erreur, ainsi, dès que vous devez ajouter plusieurs règles simples ou complexes, cela va devenir un cauchemar pour le code/déboguer/maintenir/modifier ... 

Ce que j'ai fait (et cela fonctionne exceptionnellement bien) est de créer des classes strictes/concrètes qui étendent une règle abstraite (1 pour chaque type de règle). Chaque implémentation sait quelles informations elle requiert et comment traiter ces informations pour obtenir le résultat souhaité.

Sur le Web/le front-end, vous allez créer un composant (pour chaque implémentation de règle) qui correspond strictement à cette règle. Vous pouvez ensuite donner à l'utilisateur l'option de la règle qu'il souhaite utiliser et mettre à jour l'interface en conséquence (par rechargement de page/javascript).

Lorsque la règle est ajoutée/modifiée, parcourez toutes les implémentations de règles pour obtenir l'implémentation correspondante et demandez à cette implémentation d'analyser les données brutes (id recommend using json) depuis le serveur frontal, puis d'exécuter cette règle.

public abstract class AbstractRule{
  public boolean canHandle(JSONObject rawRuleData){
    return StringUtils.equals(getClass().getSimpleName(), rawRuleData.getString("ruleClassName"));
  }
  public abstract void parseRawRuleDataIntoThis(JSONObject rawRuleData); //throw some validation exception
  public abstract RuleResult execute();
}
public class InOutPatientRule extends AbstractRule{
  private String patientType;
  private String admissionType;

  public void parseRawRuleDataIntoThis(JSONObject rawRuleData){
    this.patientType = rawRuleData.getString("patientType");
    this.admissionType= rawRuleData.getString("admissionType");
  }
  public RuleResultInOutPatientType execute(){
    if(StringUtils.equals("A",this.patientType) && StringUtils.equals("O",this.admissionType)){
      return //OUTPATIENT
    }
    return //INPATIENT
  }
}
5
NeilA

Plutôt que de créer votre propre moteur de règles, envisagez le moteur Open Source N-CUBE, un moteur de règles Java Open Source qui utilise Groovy comme langage DSL (Domain Specific Language). 

C'est un moteur de règles séquentiel, par opposition à un moteur de règles non séquentiel, comme un moteur de règles basé sur RETE. L'avantage d'un moteur de règles séquentielles est qu'il est beaucoup plus facile de déboguer les règles. Il peut être très difficile d'essayer de déchiffrer des inférences à partir de jeux de règles très volumineux, mais avec un moteur de règles séquentiel tel que N-CUBE, le traçage des règles est très similaire à la logique séquentielle de code suivante.

N-CUBE prend en charge les tables de décision et les arbres de décision. Les tables de décision et les arbres dans N-CUBE permettent aux données ou au code de s'exécuter dans les cellules, à la manière d'un Excel multidimensionnel. Le langage 'macro' (DSL) est Groovy. Lors de l'écriture de code dans une cellule, il n'est pas nécessaire de définir une instruction de package, des importations, un nom de classe ou une fonction - tout cela est ajouté pour vous, ce qui facilite la lecture/écriture des extraits de code DSL.

Ce moteur de règles est disponible sur GitHub à l’adresse https://github.com/jdereg/n-cube .

3
John DeRegnaucourt

C'est ce que je ferais. Je crée un ensemble de variables de regex, en fonction de la correspondance, je code la logique métier. Si le jeu de règles est complexe, je choisirais l'implémentation des communes Apache CommandLineParser sur le serveur. 

Mais vous pouvez utiliser GUI/HTML et un ensemble de listes déroulantes et de listes déroulantes. De cette façon, vous pouvez faire des requêtes de base de données clairement.

2
Siva Tumma

Au lieu de textArea, fournit est comme une boîte de choix pour les opérateurs à état fixe (PATIENT_TYPE) et fixe () et vous en aurez terminé avec cela . Quoi qu'il en soit, vous contrôlez l'aspect de l'application Web.

2
Sathya

Un simple moteur de règles peut être construit sur des fermetures, c'est-à-dire dans Groovy: 

def sendToOutPatient = { ... };

def sendToInPatient = { ... };

def patientRule = { PATIENT_TYPE ->
    {'A': sendToOutPatient,
     'B': sendToInPatient}.get(PATIENT_TYPE)
}

static main(){
    (patientRule('A'))()
}

Vous pouvez définir vos règles comme des fermetures, les réutiliser/les réaffecter ou même créer un DSL par-dessus.

Et Groovy peut être facilement intégré à Java, par exemple:

GroovyShell Shell = new GroovyShell(binding);
binding.setVariable("foo", "World");
System.out.println(Shell.evaluate("println 'Hello ${foo}!';));
2
Andrey Chaschev

Parlez bien avec vos utilisateurs en leur demandant pourquoi cela doit être configurable et quels changements dans la configuration ils prévoient être. Découvrez quels changements à venir sont certains, probables, possibles, lointains et incroyablement improbables. Et à quelle vitesse ils devraient être mis en œuvre. Pour chaque modification, l'écriture d'une petite mise à jour serait-elle acceptable ou non?

En gardant à l'esprit cette souplesse nécessaire, évaluez la possibilité de lancer votre propre solution par rapport à l'intégration d'un moteur complet. "Testez" votre solution simple par rapport aux scénarios de changement à venir en indiquant brièvement comment chaque changement serait mis en œuvre. Ce n'est pas grave si certains scénarios improbables ont un coût élevé. Si les scénarios probables sont également coûteux, vous feriez bien de choisir une solution plus générique. 

En ce qui concerne les options à considérer, j'aime bien les bave et la suggestion d'écrire les vôtres. Une troisième option: lors de l’implémentation d’un paquet d’enregistrement financier avec des mises à jour légales annuelles, nous avons très bien réussi à implémenter les règles dans le code mais en laissant leurs paramètres configurables dans des tables SQL. Donc, dans votre cas, cela pourrait signifier une table comme ceci:

patient_type | admission_type | inpatient_or_outpatient
-------------------------------------------------------
'A'          | 'O'            | 'Outpatient'
'B'          | NULL           | 'Inpatient'

(Nos tables ont généralement des colonnes date-début et date-validité qui permettent à l'utilisateur de mettre en scène les modifications)

Si vous écrivez un ADSL, jetez un coup d’œil à http://martinfowler.com/books/dsl.html qui offre une description détaillée des différentes approches .. Section Questions-Réponses Martin Fowler écrit:

C'est donc le crochet - les hommes d'affaires écrivent les règles eux-mêmes?

En général, je ne le pense pas. Créer un environnement représente beaucoup de travail cela permet aux gens d'affaires d'écrire leurs propres règles. Vous devez faire un outil d'édition confortable, des outils de débogage, des outils de test, etc. . Vous tirez le meilleur parti des avantages des solutions DSL en faisant suffisamment pour que permettre aux gens d'affaires de pouvoir lire les règles. Ils peuvent alors passez-les en revue pour en vérifier l'exactitude, parlez-en avec les développeurs et Brouillons des modifications que les développeurs doivent implémenter correctement. Faire en sorte que les DSL soient lisible pour les entreprises demande beaucoup moins d'effort que pour les rédacteurs, mais donne la plupart des avantages. Il y a des moments où cela vaut la peine d'être fait l'effort de rendre les DSL inscriptibles, mais c'est un plus objectif avancé.

1
flup

Comme l'analyse du code avec Java uniquement est une implémentation suicide, vous pouvez écrire un compilateur simple à l'aide de Jflex et CUP , qui sont la version Java de GNU FLEX et YACC. De cette façon, vous pouvez générer des jetons simples avec Jflex (un jeton est un mot clé comme IF, ELSE etc.) pendant que CUP consomme ces jetons pour exécuter du code.

1
HAL9000

Il existe un moteur de règles pour Clojure appelé Clara , qui peut être utilisé depuis Java ainsi que Clojure [Java] Script. Je pense qu'il serait assez facile de créer quelque chose de utilisable à partir de cela.

1
claj

L'implémentation d'un moteur de règles est not trivial. Un système significatif basé sur des règles possède un moteur d'inférence qui prend en charge à la fois les stratégies de chaînage avant et arrière, ainsi que les stratégies de recherche en largeur d'abord et en profondeur. Easy Rules n’a rien de tout cela, il n’exécute toutes les règles qu’une seule fois. Drools prend en charge l’enchaînement en avant et en arrière et, autant que je sache, la profondeur d'abord. C'est expliqué ici .

D'après mon expérience, Drools est le seul moteur de règles significatif pour Java. Il a ses limites. Je dois dire que j'ai utilisé Drools il y a plus de 5 ans. 

0
Christine