Interpreter

La programmation avec design patterns

Ce site ne sera plus alimenté de contenu après août 2014. Tous les nouveaux articles seront redigés pour www.waitingforcode.com
L'interprétation n'appartient pas qu'à la domaine des sciences humaines où les étudiants tentent de deviner ce que voulait dire Goethe dans ses oeuvres tristes. L'interprétation a également sa place dans le développement des applications où l'on nécessite de traduire des symboles en objets informatiques pour effectuer des opérations spécifiques.

Il y a même un patron de conception qui traite de cette problématique. Il s'appelle interpreter (interprète). Son rôle principale consiste à déterminer comment évaluer des expressions dans un langage.

L'interprète se base sur les représentations grammaires d'un langage. Elles sont ensuite interprétées par l'interprète. L'image de l'interprète dans l'informatique est exactement le même que celui d'un traducteur dans le monde réel. Le traducteur connaît d'habitude une langue étrangère à une ou plusieurs d'autres personnes. Il se charge ensuite de comprendre ce qui est dit et de l'intérpréter à sa façon. L'interprétation est ensuite transmise à des personnes n'ayant pas de connaissance de la langue étrangère.

Parmi les acteurs participant dans ce patron de conception on distingue :
- expression base (base d'expression) : la classe de base pour toutes les expressions. C'est elle qui définit les méthodes d'interprétation.
- terminal expression (expression finale) : cette classe représente les expressions comprises dans un seul objet étant directement l'enfant de la classe de base.
- nonterminal expression (expression non finale) : cette classe représente les expressions non finale. Ce type d'expressions est composé de plusieurs autres expressions, finales ou non finales. La différence avec l'expression finale est donc le fait que l'expression non finale peut contenir plusieurs expressions.
- contex (contexte) : le contexte stocke toutes les informations dont les expressions peuvent avoir besoin pour effectuer leurs opérations.
- client : c'est lui qui joue le rôle de l'interprète.

Exemple d'interpreter
Pour mieux voir toutes les spécialités de l’interpréter, abordons un exemple. L'exemple représentera la traduction d'une expressions SQL-semblables à l'expression prête à être utilisée dans le moteur de bases de données MySQL.

interface SqlExpression {

public String evaluate(String values);
}

class Select implements SqlExpression {

@Override
public String evaluate(String values) {
return "SELECT";
}

}

class Fields implements SqlExpression {

@Override
public String evaluate(String values) {
return values;
}

}

class From implements SqlExpression {

@Override
public String evaluate(String values) {
return "FROM";
}

}

class Table implements SqlExpression {

@Override
public String evaluate(String values) {
return values;
}

}

class Where implements SqlExpression {

@Override
public String evaluate(String values) {
return "WHERE";
}

}

class Conditions implements SqlExpression {
private Where where = new Where();

@Override
public String evaluate(String values) {
return where.evaluate(values) + " " + values;
}

}

L'interface présente la base d'expression. Sa seule méthode evaluate() va contenir le texte à retourner par toutes les implémentations du SqlExpression. Ensuite toutes les autres classes sont des expressions terminales. Juste la dernière, Conditions, reflète en quelque sorte l'expression non-terminale. Elle est composée des 2 expressions : elle-même et Where, jamais appelée directement.


class SqlInterpreter implements SqlExpression {
private Context context;

public SqlInterpreter(Context context) {
this.context = context;
}

@Override
public String evaluate(String values) {
String[] params = this.context.getInput().split(this.context.getSeparator());
for(String sentence : params) {
String q = sentence;
String v = "";
if (q.indexOf("EQUALS") > -1) {
String[] p = q.split("EQUALS");
q = p[0];
v = p[1];
}
SqlExpression e = null;
if (q.equals("s")) {
e = new Select();
} else if (q.equals("f")) {
e = new Fields();
} else if (q.equals("f")) {
e = new From();
} else if (q.equals("t")) {
e = new Table();
} else if (q.equals("c")) {
e = new Conditions();
}
if (e != null) {
String o = this.context.getOutput();
this.context.setOutput(o + e.evaluate(v) + " ");
}
}
return this.context.getOutput();
}

}

Il s'agit de l'interprète. C'est lui qui traduit la map en requête SQL. Le code présenté ci-dessus utilise le contexte présenté plus bas dans l'article.


class Context {
private String input;
private String output;

public Context(String input) {
this.input = input;
this.output ="";
}

public void setOutput(String output) {
this.output = output;
}

public String getOutput() {
return this.output;
}

public String getInput() {
return this.input;
}

public String getSeparator() {
return ";";
}
}


On voit que le context contient les données d'entrée et de sortie ainsi qu'un dénominateur commun qui permet de séparer les valeurs en fonction des classes d'expression (méthode getSeparator()).


Context context = new Context("s;fEQUALSname,bornDate;f;tEQUALSpeople;cEQUALSname LIKE 'Syl%' AND gender = 'F'");
SqlInterpreter interpreter = new SqlInterpreter(context);
System.out.println("SQL query is : " + interpreter.evaluate(""));



SQL query is : SELECT name,bornDate people WHERE name LIKE 'Syl%' AND gender = 'F'



L'interprète est un patron de conception qui permet de traduire une expression imaginaire en expression compréhensible par un système. C'est applicable aussi bien dans nos exemples de traduction qu'à toutes les "pseudo-langages" d'expressions, comme par exemple Spring Expression Language.
Bartosz KONIECZNY 06-10-2013 16:39 design patterns
Moi

Développeur d'applications Internet et journaliste passionné par l'adjectif français. Un aigle polonais orienté vers la progression, volant très haut et écoutant du zouk après les matches du foot français.

Vous appréciez mon travail ?

Pour contribuer au développement de ce site, ou pour remercier pour des articles rédigés, vous pouvez faire un don.

Un conseil MySQL

Comment sommer les champs du type TIME ?

La requête suivante devrait être utile :
SELECT SEC_TO_TIME(SUM(TIME_TO_SEC(maColonne))) AS somme FROM nomTableau;