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.