El patrón Interpreter (Intérprete) proporciona una manera de evaluar sentencias en un lenguaje, permitiendo interpretar dichas sentencias dentro del programa.
Complejo, ahora de forma simple: Supongamos que tenemos un libro de instrucciones para armar un juguete, pero está escrito en un idioma que no entendemos. El Interpreter es como un traductor, toma esas instrucciones complicadas y las convierte en pasos simples que podemos entender y seguir.
El problema que resuelve es la necesidad de interpretar y procesar lenguajes o expresiones definidos por reglas gramaticales o sintácticas, especialmente en dominios como compiladores, motores de reglas o procesadores de lenguajes de programación y scripting.
El patrón Interpreter soluciona este problema implementando una clase para cada regla gramatical en el lenguaje. Cada regla se expresa como una expresión que puede ser interpretada para evaluar la sentencia.
Al hablar del patrón Interpreter, debemos mencionar el Arbol de Sintaxis Abstracta (AST), una representación clave de la estructura de una expresión.
Un AST es una estructura de árbol donde cada nodo representa una operación o entidad en la expresión, como un número o una operación matemática:
Cuando se usa el patrón Interpreter para interpretar una expresión, generalmente se construye un AST como parte del proceso de interpretación.
Cada tipo de expresión en la gramática del lenguaje (números, operaciones matemáticas, etc.) tiene una clase correspondiente en el sistema, y cada instancia de esas clases representa un nodo en el AST.
Por ejemplo, para la expresión “1 + 2”:
Se crea un nodo hoja para ‘1’.
Se crea un nodo hoja para ‘2’.
Se crea un nodo interno para ‘+’, con los nodos ‘1’ y ‘2’ como hijos.
Este árbol se construye analizando la expresión y creando los nodos correspondientes. Luego, para interpretar la expresión, el sistema recorrería este árbol y realizaría las operaciones definidas en cada nodo.
El uso del árbol permite al sistema descomponer las expresiones en partes manejables y luego interpretarlas de una manera estructurada y coherente.
Este patrón es recomendable cuando:
Flexibilidad: Permite interpretar nuevos tipos de expresiones.
Extensibilidad: Fácil de ampliar y modificar el lenguaje o gramática.
Complejidad: Puede volverse complejo si la gramática del lenguaje es complicada.
Rendimiento: Interpretar expresiones puede ser menos eficiente que otros métodos de procesamiento.
Debemos crear un sistema educativo cuyo propósito es interpretar y evaluar expresiones matemáticas simples ingresadas por los usuarios.
Estas expresiones pueden incluir operaciones básicas como suma, resta, multiplicación y división.
El sistema debe ser capaz de entender y procesar una variedad de expresiones matemáticas ingresadas en formato texto. Implementar un enfoque directo para interpretar estas expresiones puede resultar en un código complejo y difícil de mantener, especialmente al añadir más operaciones o cambiar la gramática de las expresiones.
Elegimos utilizar el patrón Interpreter porque ofrece una solución estructurada y extensible para interpretar lenguajes. Definimos una gramática para las expresiones matemáticas, construimos un intérprete que entienda esa gramática y luego lo usamos para interpretar las expresiones en el lenguaje definido.
Vamos a interpretar una expresión matemática básica: “1 + 2”
En este caso:
Al usar el patrón Interpreter descomponemos esta expresión en partes que el sistema puede entender y procesar una por una.
Interpretar “1”: El sistema ve el primer ‘1’ y lo reconoce como un número. Utilizamos un NumberExpression para representar este número.
Interpretar “+”: Luego ve el símbolo ‘+’, que representa la operación de suma. El sistema utiliza una AddExpression para prepararse para sumar los números que encuentra.
Interpretar “2”: Finalmente, ve el ‘2’ y lo reconoce como otro número, utilizando otro NumberExpression para representarlo.
Calcular el Resultado: Ahora, el sistema tiene todo lo que necesita para calcular el resultado. La AddExpression toma los dos NumberExpression (representando ‘1’ y ‘2’) y los suma, produciendo el resultado ‘3’.
En resumen, el sistema toma la expresión “1 + 2”, la descompone en partes manejables (‘1’, ‘+’, ‘2’), y luego las procesa paso a paso para llegar al resultado de ‘3’. Todo esto se hace siguiendo las reglas y la estructura definidas en el patrón Interpreter para entender y procesar la expresión.
Declaramos la interfaz Expression con el método interpret. Implementamos las clases NumberExpression y AddExpression, por un lado la lógica para interpretar números y por otro la operación de suma. El cliente construye una expresión y luego la evalúa llamando al método interpret().
Creamos la Interfaz Expression:
interface Expression {
int interpret();
}
Implementamos el Terminal Expression:
class NumberExpression implements Expression {
private int number;
public NumberExpression(int number) {
this.number = number;
}
@Override
public int interpret() {
return number;
}
}
Creamos un Non Terminal Expression:
class AddExpression implements Expression {
private Expression firstExpression, secondExpression;
public AddExpression(Expression first, Expression second) {
this.firstExpression = first;
this.secondExpression = second;
}
@Override
public int interpret() {
return firstExpression.interpret() + secondExpression.interpret();
}
}
// Puedo agregar clases para restar, multiplicar, etc.
El código cliente:
public class Client {
public static void main(String[] args) {
Expression expression = new AddExpression(new NumberExpression(1), new NumberExpression(2));
int result = expression.interpret();
System.out.println("El resultado es: " + result);
}
}
Los participantes que vimos antes son: AbstractExpression, TerminalExpression, NonTerminalExpression, Context, Client:
Este ejemplo muestra cómo usar el patrón Interpreter para construir y evaluar una expresión matemática.
Este enfoque nos dió flexibilidad para extenderlo (podemos agregar fácilmente otras operaciones).
Se puede usar para construir la estructura de árbol de sintaxis abstracta.
Puede utilizarse para compartir instancias de TerminalExpression.