PHP Interpreter Design Pattern Поведенческий паттерн

Паттерн Интерпретатор (Interpreter) в PHP

Короткий скринкаст: паттерн «Интерпретатор» (Interpreter) — грамматика языка выражается деревом объектов, и каждый узел умеет вычислить себя. Строим выражение (5 + 3) * 2 и получаем 16.

Interpreter Pattern — открыть страницу урока

Описание

Интерпретатор описывает грамматику языка набором классов и учит каждый узел дерева вычислять себя. Терминальный узел Number хранит готовое значение, а нетерминальные Add и Multiply держат по два вложенных выражения и рекурсивно просят их посчитать. Клиент собирает дерево (5 + 3) * 2 и вызывает interpret() только у корня — дальше вычисление растекается по дереву само. Чтобы добавить новое правило языка, достаточно реализовать Expression ещё одним классом.

UML-диаграмма

classDiagram class Expression { <<interface>> +interpret(): int } class Number { -value: int +interpret(): int } class Add { -left: Expression -right: Expression +interpret(): int } class Multiply { -left: Expression -right: Expression +interpret(): int } Expression <|-- Number : implements Expression <|-- Add : implements Expression <|-- Multiply : implements Add o-- Expression : children Multiply o-- Expression : children

Код

Expression.php

interface Expression
{
    public function interpret(): int;
}

Number.php

class Number implements Expression
{
    public function __construct(private int $value) {}

    public function interpret(): int
    {
        return $this->value;
    }
}

Add.php

class Add implements Expression
{
    public function __construct(private Expression $left, private Expression $right) {}

    public function interpret(): int
    {
        return $this->left->interpret() + $this->right->interpret();
    }
}

Multiply.php

class Multiply implements Expression
{
    public function __construct(private Expression $left, private Expression $right) {}

    public function interpret(): int
    {
        return $this->left->interpret() * $this->right->interpret();
    }
}

demo.php

require_once 'Expression.php';
require_once 'Number.php';
require_once 'Add.php';
require_once 'Multiply.php';

$add = new Add(new Number(5), new Number(3));
$multiply = new Multiply($add, new Number(2));

echo $multiply->interpret().PHP_EOL;

Пример результата

После php demo.php дерево само вычисляет себя: сложение вложено в умножение, и корневой interpret() возвращает результат (5 + 3) * 2:

16

Преимущества

  • Грамматика описывается явными классами, а не разбором строк «на месте».
  • Новое правило языка — это просто новое выражение, существующий код не меняется.
  • Правила легко комбинировать: любое выражение можно вложить в другое.
  • Каждый узел отвечает только за своё правило (принцип единственной обязанности).

Когда использовать

  • Когда есть простой язык или нотация, которую нужно интерпретировать: запросы, условия, формулы.
  • Когда грамматика невелика и стабильна, а правила удобно выразить классами.
  • Когда одно и то же правило нужно переиспользовать внутри других выражений.
  • Когда вычисление естественно ложится на дерево объектов с рекурсией.

Использование в Laravel

В Laravel Интерпретатор встречается там, где правило превращается в объект с методом вычисления. Классический пример — правила валидации (Illuminate\Contracts\Validation\Rule и Illuminate\Validation\Rules\*): каждое правило инкапсулирует свою логику и умеет проверить значение. Тот же подход — у Illuminate\Database\Query\Expression и правил БД, а также у фильтров запросов, где набор условий собирается как дерево и вычисляется при построении SQL.