Паттерн Interpreter (Интерпретатор) описывает способ представления грамматики простого языка и интерпретации предложений этого языка. Он полезен, когда часто требуется разбирать и выполнять выражения фиксированной, но расширяемой грамматики: фильтры, поисковые запросы, правила доступа.
Когда применять Interpreter
- Грамматика проста и часто меняется, нужно легко добавлять новые правила.
- Выражений много, но каждое небольшое — важна скорость разработки, а не максимальная производительность.
- Нужно отделить описание языка (дерево выражений) от его интерпретации (контекст и вычисление).
PlantUML-диаграмма
// Пример мини-языка логических выражений:
// Поддерживаем операции AND, OR, NOT и терминалы: переменные/константы (true/false).
// Контекст — хранит значения переменных
public class Context
{
private readonly Dictionary<string, bool> _vars = new();
public void Set(string name, bool value) => _vars[name] = value;
public bool Get(string name) => _vars.TryGetValue(name, out var v) ? v : false;
}
// Абстрактное выражение
public interface IExpression
{
bool Interpret(Context context);
}
// Терминалы
public class Constant : IExpression
{
private readonly bool _value;
public Constant(bool value) => _value = value;
public bool Interpret(Context context) => _value;
}
public class Variable : IExpression
{
private readonly string _name;
public Variable(string name) => _name = name;
public bool Interpret(Context context) => context.Get(_name);
}
// Нетерминалы (правила грамматики)
public class NotExpression : IExpression
{
private readonly IExpression _expr;
public NotExpression(IExpression expr) => _expr = expr;
public bool Interpret(Context context) => !_expr.Interpret(context);
}
public class AndExpression : IExpression
{
private readonly IExpression _left, _right;
public AndExpression(IExpression left, IExpression right)
{
_left = left; _right = right;
}
public bool Interpret(Context context) => _left.Interpret(context) && _right.Interpret(context);
}
public class OrExpression : IExpression
{
private readonly IExpression _left, _right;
public OrExpression(IExpression left, IExpression right)
{
_left = left; _right = right;
}
public bool Interpret(Context context) => _left.Interpret(context) || _right.Interpret(context);
}
// Пример парсинга простейшей записи вручную (в проде — парсер)
class Program
{
static void Main()
{
// Выражение: (isAdmin AND NOT isBanned) OR isModerator
IExpression expr =
new OrExpression(
new AndExpression(
new Variable("isAdmin"),
new NotExpression(new Variable("isBanned"))
),
new Variable("isModerator")
);
var ctx = new Context();
ctx.Set("isAdmin", true);
ctx.Set("isBanned", false);
ctx.Set("isModerator", false);
Console.WriteLine($"Результат: {expr.Interpret(ctx)}"); // true
ctx.Set("isBanned", true);
Console.WriteLine($"Результат: {expr.Interpret(ctx)}"); // false
ctx.Set("isModerator", true);
Console.WriteLine($"Результат: {expr.Interpret(ctx)}"); // true
}
}
type Context struct {
vars map[string]bool
}
func NewContext() *Context { return &Context{vars: map[string]bool{}} }
func (c *Context) Set(name string, value bool) { c.vars[name] = value }
func (c *Context) Get(name string) bool { return c.vars[name] }
type Expression interface {
Interpret(ctx *Context) bool
}
type Constant bool
func (c Constant) Interpret(*Context) bool { return bool(c) }
type Variable string
func (v Variable) Interpret(ctx *Context) bool { return ctx.Get(string(v)) }
type Not struct{ Expr Expression }
func (n Not) Interpret(ctx *Context) bool { return !n.Expr.Interpret(ctx) }
type And struct{ Left, Right Expression }
func (a And) Interpret(ctx *Context) bool {
return a.Left.Interpret(ctx) && a.Right.Interpret(ctx)
}
type Or struct{ Left, Right Expression }
func (o Or) Interpret(ctx *Context) bool {
return o.Left.Interpret(ctx) || o.Right.Interpret(ctx)
}
func main() {
// (isAdmin AND NOT isBanned) OR isModerator
var expr Expression = Or{
Left: And{
Left: Variable("isAdmin"),
Right: Not{Expr: Variable("isBanned")},
},
Right: Variable("isModerator"),
}
ctx := NewContext()
ctx.Set("isAdmin", true)
ctx.Set("isBanned", false)
ctx.Set("isModerator", false)
fmt.Printf("Результат: %t\n", expr.Interpret(ctx))
ctx.Set("isBanned", true)
fmt.Printf("Результат: %t\n", expr.Interpret(ctx))
ctx.Set("isModerator", true)
fmt.Printf("Результат: %t\n", expr.Interpret(ctx))
}
from typing import Protocol
class Context:
def __init__(self) -> None:
self._vars: dict[str, bool] = {}
def set(self, name: str, value: bool) -> None:
self._vars[name] = value
def get(self, name: str) -> bool:
return self._vars.get(name, False)
class Expression(Protocol):
def interpret(self, context: Context) -> bool: ...
class Constant:
def __init__(self, value: bool) -> None:
self._value = value
def interpret(self, context: Context) -> bool:
return self._value
class Variable:
def __init__(self, name: str) -> None:
self._name = name
def interpret(self, context: Context) -> bool:
return context.get(self._name)
class Not:
def __init__(self, expr: Expression) -> None:
self._expr = expr
def interpret(self, context: Context) -> bool:
return not self._expr.interpret(context)
class And:
def __init__(self, left: Expression, right: Expression) -> None:
self._left = left
self._right = right
def interpret(self, context: Context) -> bool:
return self._left.interpret(context) and self._right.interpret(context)
class Or:
def __init__(self, left: Expression, right: Expression) -> None:
self._left = left
self._right = right
def interpret(self, context: Context) -> bool:
return self._left.interpret(context) or self._right.interpret(context)
# (isAdmin AND NOT isBanned) OR isModerator
expr: Expression = Or(And(Variable("isAdmin"), Not(Variable("isBanned"))), Variable("isModerator"))
ctx = Context()
ctx.set("isAdmin", True)
ctx.set("isBanned", False)
ctx.set("isModerator", False)
print(f"Результат: {expr.interpret(ctx)}")
ctx.set("isBanned", True)
print(f"Результат: {expr.interpret(ctx)}")
ctx.set("isModerator", True)
print(f"Результат: {expr.interpret(ctx)}")
class Context {
private readonly vars = new Map<string, boolean>();
set(name: string, value: boolean): void {
this.vars.set(name, value);
}
get(name: string): boolean {
return this.vars.get(name) ?? false;
}
}
interface Expression {
interpret(context: Context): boolean;
}
class Constant implements Expression {
constructor(private readonly value: boolean) {}
interpret(): boolean {
return this.value;
}
}
class Variable implements Expression {
constructor(private readonly name: string) {}
interpret(context: Context): boolean {
return context.get(this.name);
}
}
class Not implements Expression {
constructor(private readonly expr: Expression) {}
interpret(context: Context): boolean {
return !this.expr.interpret(context);
}
}
class And implements Expression {
constructor(
private readonly left: Expression,
private readonly right: Expression,
) {}
interpret(context: Context): boolean {
return this.left.interpret(context) && this.right.interpret(context);
}
}
class Or implements Expression {
constructor(
private readonly left: Expression,
private readonly right: Expression,
) {}
interpret(context: Context): boolean {
return this.left.interpret(context) || this.right.interpret(context);
}
}
// (isAdmin AND NOT isBanned) OR isModerator
const expr: Expression = new Or(
new And(new Variable("isAdmin"), new Not(new Variable("isBanned"))),
new Variable("isModerator"),
);
const ctx = new Context();
ctx.set("isAdmin", true);
ctx.set("isBanned", false);
ctx.set("isModerator", false);
console.log(`Результат: ${expr.interpret(ctx)}`);
ctx.set("isBanned", true);
console.log(`Результат: ${expr.interpret(ctx)}`);
ctx.set("isModerator", true);
console.log(`Результат: ${expr.interpret(ctx)}`);
import java.util.HashMap;
import java.util.Map;
class Context {
private final Map<String, Boolean> vars = new HashMap<>();
void set(String name, boolean value) {
vars.put(name, value);
}
boolean get(String name) {
return vars.getOrDefault(name, false);
}
}
interface Expression {
boolean interpret(Context context);
}
record Constant(boolean value) implements Expression {
public boolean interpret(Context context) {
return value;
}
}
record Variable(String name) implements Expression {
public boolean interpret(Context context) {
return context.get(name);
}
}
record Not(Expression expr) implements Expression {
public boolean interpret(Context context) {
return !expr.interpret(context);
}
}
record And(Expression left, Expression right) implements Expression {
public boolean interpret(Context context) {
return left.interpret(context) && right.interpret(context);
}
}
record Or(Expression left, Expression right) implements Expression {
public boolean interpret(Context context) {
return left.interpret(context) || right.interpret(context);
}
}
public class Program {
public static void main(String[] args) {
// (isAdmin AND NOT isBanned) OR isModerator
Expression expr = new Or(
new And(new Variable("isAdmin"), new Not(new Variable("isBanned"))),
new Variable("isModerator"));
Context ctx = new Context();
ctx.set("isAdmin", true);
ctx.set("isBanned", false);
ctx.set("isModerator", false);
System.out.println("Результат: " + expr.interpret(ctx));
ctx.set("isBanned", true);
System.out.println("Результат: " + expr.interpret(ctx));
ctx.set("isModerator", true);
System.out.println("Результат: " + expr.interpret(ctx));
}
}
class Context {
private val vars = mutableMapOf<String, Boolean>()
fun set(name: String, value: Boolean) {
vars[name] = value
}
fun get(name: String): Boolean = vars[name] ?: false
}
// Грамматика как sealed-иерархия: компилятор проверит, что разобраны все узлы.
sealed interface Expression {
fun interpret(context: Context): Boolean
}
data class Constant(val value: Boolean) : Expression {
override fun interpret(context: Context) = value
}
data class Variable(val name: String) : Expression {
override fun interpret(context: Context) = context.get(name)
}
data class Not(val expr: Expression) : Expression {
override fun interpret(context: Context) = !expr.interpret(context)
}
data class And(val left: Expression, val right: Expression) : Expression {
override fun interpret(context: Context) = left.interpret(context) && right.interpret(context)
}
data class Or(val left: Expression, val right: Expression) : Expression {
override fun interpret(context: Context) = left.interpret(context) || right.interpret(context)
}
fun main() {
// (isAdmin AND NOT isBanned) OR isModerator
val expr: Expression = Or(
And(Variable("isAdmin"), Not(Variable("isBanned"))),
Variable("isModerator"),
)
val ctx = Context()
ctx.set("isAdmin", true)
ctx.set("isBanned", false)
ctx.set("isModerator", false)
println("Результат: ${expr.interpret(ctx)}")
ctx.set("isBanned", true)
println("Результат: ${expr.interpret(ctx)}")
ctx.set("isModerator", true)
println("Результат: ${expr.interpret(ctx)}")
}
Плюсы и минусы
| Плюсы | Минусы |
|---|---|
| Простое добавление новых правил/выражений путём создания классов | Много мелких классов, усложняющих структуру проекта |
| Чёткое разделение грамматики и интерпретации | Низкая производительность на больших выражениях (глубокие деревья) |
| Подходит для DSL, фильтров, правил валидации/доступа | Требует парсера для полноценного языка |
Заключение
Interpreter удобен для небольших предметно-ориентированных языков и систем правил, где важна расширяемость и читабельность грамматики. Для сложных и высоконагруженных сценариев лучше использовать специализированные парсеры, компиляторы или механизмы правил.