Problema
Permitir que usuarios configuren reglas o fórmulas complejas sin programar.
Propósito
Convierte expresiones de texto (como “age >= 18 AND income > 30000”) en objetos que pueden evaluarse dinámicamente. Crea un “mini-lenguaje” para que usuarios configuren reglas de negocio.
Concepto clave
Árbol de expresión: Cada parte de la expresión (números, operadores, variables) se convierte en un objeto que sabe cómo evaluarse a sí mismo.
Casos de uso comunes
- Calculadoras con expresiones matemáticas
- Motores de reglas de negocio
- Lenguajes de consulta simples
- Parsers de configuración
- Validadores de expresiones regulares
- Intérpretes de comandos
¿Quién es quién en Interpreter?
Actor | Lo que realmente es | Ejemplo | Analogía |
---|---|---|---|
AbstractExpression | Interfaz que define interpret(context) |
Expression - define cómo evaluarse |
“Elemento de fórmula” (interfaz) |
TerminalExpression | Elementos básicos (hojas del árbol) | NumberExpression , VariableExpression |
Números en pantalla (3, 5, x) |
NonTerminalExpression | Operadores (nodos del árbol) | AddExpression , MultiplyExpression |
Botones de operación (+, -, *, /) |
Context | Almacena variables y valores | Context con variables x=3, y=5 |
Calculadora con memoria |
Client | Construye y evalúa el árbol | Parser + evaluador | Persona usando calculadora |
Diagrama
classDiagram
namespace InterpreterPattern {
class AbstractExpression {
<<interface>>
+interpret(context) Result
}
class TerminalExpression {
+interpret(context) Result
}
class NonTerminalExpression {
-expression1: AbstractExpression
-expression2: AbstractExpression
+interpret(context) Result
}
class Context {
-variables: Map~String, Value~
+getValue(variable) Value
+setValue(variable, value)
}
class Client {
+buildSyntaxTree() AbstractExpression
+interpret()
}
}
AbstractExpression <|.. TerminalExpression
AbstractExpression <|.. NonTerminalExpression
NonTerminalExpression --> AbstractExpression
Client --> AbstractExpression
Client --> Context
Ejemplo práctico
classDiagram
namespace CalculatorExample {
class Expression {
<<interface>>
+interpret(context) double
}
class NumberExpression {
-number: double
+interpret(context) double
}
class VariableExpression {
-variable: String
+interpret(context) double
}
class AddExpression {
-left: Expression
-right: Expression
+interpret(context) double
}
class SubtractExpression {
-left: Expression
-right: Expression
+interpret(context) double
}
class MultiplyExpression {
-left: Expression
-right: Expression
+interpret(context) double
}
class Context {
-variables: Map~String, Double~
+getValue(variable) Double
+setValue(variable, value)
}
class ExpressionParser {
+parse(expression) Expression
}
}
Expression <|.. NumberExpression
Expression <|.. VariableExpression
Expression <|.. AddExpression
Expression <|.. SubtractExpression
Expression <|.. MultiplyExpression
AddExpression --> Expression
SubtractExpression --> Expression
MultiplyExpression --> Expression
ExpressionParser --> Expression
Árbol de sintaxis
graph TB
A["+ (AddExpression)"] --> B["* (MultiplyExpression)"]
A --> C["5 (NumberExpression)"]
B --> D["x (VariableExpression)"]
B --> E["2 (NumberExpression)"]
subgraph "Expression: x * 2 + 5"
F["Context: x = 3"]
end
subgraph "Evaluation"
G["x * 2 = 3 * 2 = 6"]
H["6 + 5 = 11"]
end
Flujo de interpretación
sequenceDiagram
participant Client
participant Parser
participant AddExpr as AddExpression
participant MultExpr as MultiplyExpression
participant VarExpr as VariableExpression
participant NumExpr as NumberExpression
participant Context
Client->>Parser: parse("x * 2 + 5")
Parser->>AddExpr: new AddExpression(mult, num5)
Parser->>MultExpr: new MultiplyExpression(varX, num2)
Parser-->>Client: expression tree
Client->>Context: setValue("x", 3)
Client->>AddExpr: interpret(context)
AddExpr->>MultExpr: interpret(context)
MultExpr->>VarExpr: interpret(context)
VarExpr->>Context: getValue("x")
Context-->>VarExpr: 3
VarExpr-->>MultExpr: 3
MultExpr->>NumExpr: interpret(context)
NumExpr-->>MultExpr: 2
MultExpr-->>AddExpr: 6
AddExpr->>NumExpr: interpret(context)
NumExpr-->>AddExpr: 5
AddExpr-->>Client: 11
Casos de uso prácticos
// 1. Calculadora dinámica
Expression expr = parser.parse("x * 2 + 5");
Context context = new Context();
context.setValue("x", 3);
double result = expr.interpret(context); // = 11
// 2. Reglas de negocio configurables
Expression rule = parser.parse("age >= 18 AND income > 30000");
Context customer = new Context();
customer.setValue("age", 25);
customer.setValue("income", 45000);
boolean approved = rule.interpret(customer); // = true
// 3. Fórmulas de precios dinámicas
Expression pricing = parser.parse("basePrice * (1 + taxRate) - discount");
Context order = new Context();
order.setValue("basePrice", 100);
order.setValue("taxRate", 0.15);
order.setValue("discount", 10);
double finalPrice = pricing.interpret(order); // = 105
Ventajas
- Extensibilidad: Fácil agregar nuevas reglas gramaticales
- Flexibilidad: Permite cambiar y extender el lenguaje
- Reutilización: Las expresiones pueden reutilizarse
- Separación: Separa gramática de interpretación
Desventajas
- Complejidad: Muchas clases para gramáticas complejas
- Performance: Puede ser lento para expresiones complejas
- Mantenimiento: Difícil mantener gramáticas grandes
- Memoria: Consume memoria con árboles de sintaxis grandes
Cuándo usar
- Necesitas que usuarios configuren reglas sin programar
- Tienes fórmulas o expresiones que cambian frecuentemente
- Quieres crear un DSL (Domain Specific Language) simple
- Las reglas de negocio son complejas pero bien definidas
Cuándo NO usar
- Las expresiones son simples (usa Strategy)
- La performance es crítica
- El lenguaje es muy complejo (usa un parser real)
- Las reglas no cambian frecuentemente