Solution objet du TD-5

Ce sont les sources Lexeme, Lexer (qui n'ont pas changé), puis Bool, LL, Toto, Env et Pigeon.

Le principe de base, exposé par la classe Bool est de définir Bool comme une classe abstraite (c'est à dire ne faisant que définir les signatures de ses méthodes, mais pas leur code).
abstract class Bool {
   abstract boolean eval (Env e) ; // Évaluer this dans l'environnement e
   abstract void vars (Env e) ;    // Ajouter les variables de this à l'environnement e
}

Ensuite, chaque sorte de noeud est défini comme une sous-classe de Bool. Par exemple voici la classe Or :

class Or extends Bool {
  private Bool arg0, arg1 ;

  Or (Bool arg0, Bool arg1) { // Constructeur
    this.arg0 = arg0 ;
    this.arg1 = arg1 ;
  }

 /****************************************/
 /* Code des méthodes abstraites de Bool */
 /****************************************/

  boolean eval (Env e) {
    return arg0.eval(e) || arg1.eval(e) ;
  }

  void vars (Env e) {
    arg0.vars(e) ;
    arg1.vars(e) ;
  }

  // Classique
  public String toString () {
    StringBuffer r = new StringBuffer () ;

    r.append('(') ;
    r.append(arg0) ; // Appel implicite arg0.toString()
    r.append(" \\/ ") ;
    r.append(arg1) ; // Appel implicite arg1.toString()
    r.append(')') ;
    return r.toString () ;
  }
}
Dès lors, un objet de la classe Or est aussi un objet de la classe Bool.

Ainsi si on a Bool b = ..., on peut écrire b.eval(e) et si b se trouve être un Or, le mécanisme d'appel de méthode (ici réellement exploité pour la première fois) appelle la méthode eval définie dans la classe Or. Plus généralement, si b se trouve être un objet d'une quelconque des sous-classes de Bool c'est sa méthode qui sera appelée.

Un avantage de cette écriture dans le cadre Java est que la construction des noeud devient naturelle : appel d'un constructeur new Or (..., ...) par exemple. Un avantage plus général de ce style objet est qu'il est facile d'ajouter une sorte de noeud (par exemple ici, ``implique''), en créeant une nouvelle sous-classe de Bool.

Un inconvénient est qu'il n'est pas facile d'ajouter une méthode opérant sur toutes les expressions. Il faut alors modifier toutes les sous-classes de Bool et le code se trouve réparti un peu partout --- C'est pourquoi j'ai dérogé au principe ``une classe, un fichier'', en regroupant la classe Bool et toutes ses sous-classes dans le fichier Bool.java. Un autre inconvénient, plus grave, est la difficulté d'exprimer simplement dans ce style des opérations portant sur plusieurs noeuds à la fois, par exemple un simplificateur des expressions booléennes qui exploite l'associativité des connecteurs ``et'' et ``ou' logiques.

En ce qui me concerne et ça n'engage que moi, je n'aime pas trop ce style. Je préfère la programmation par filtrage comme en Caml. Mais il y a d'autres différences et le style objet est parfois utile. Ici, c'est à mon avis un peu artificiel.

1   Dernière minute

Une solution plus efficace a été trouvée par un élève. L'idée est, une fois que la valeur d'une variable est fixée de simplifier la proposition en utlisant les règles du calcul booleen. Par exemple P \/ true -> true et P \/ false -> P.

On finira alors forcément par réduire la proposition à true ou false et cela peut arriver bien avant d'énumerer toutes les valuations.

Cette idée est exploitée par la classe TotoBis et par la méthode simpl de la class Bool.


Ce document a été traduit de LATEX par HEVEA.