Интерпретатор (шаблон проектирования)
Интерпретатор | |
---|---|
Interpreter | |
Тип | поведенческий |
Назначение | решает часто встречающуюся, подверженную изменениям задачу |
Описан в Design Patterns | Да |
Интерпретатор (англ. Interpreter) — поведенческий шаблон проектирования, решающий часто встречающуюся, но подверженную изменениям, задачу. Также известен как Little (Small) Language
Проблема
[править | править код]Имеется часто встречающаяся, подверженная изменениям задача.
Решение
[править | править код]Создать интерпретатор, который решает данную задачу.
Преимущества
[править | править код]Грамматику становится легко расширять и изменять, реализации классов, описывающих узлы абстрактного синтаксического дерева похожи (легко кодируются). Можно легко изменять способ вычисления выражений.
Недостатки
[править | править код]Сопровождение грамматики с большим числом правил затруднительно.
Пример
[править | править код]Задача поиска строк по образцу может быть решена посредством создания интерпретатора, определяющего грамматику языка. "Клиент" строит предложение в виде абстрактного синтаксического дерева, в узлах которого находятся объекты классов "ТерминальноеВыражение" и "НетерминальноеВыражение" (рекурсивное), затем "Клиент" инициализирует контекст и вызывает операцию Разобрать(Контекст). На каждом узле типа "НетерминальноеВыражение" определяется операция Разобрать для каждого подвыражения. Для класса "НетерминальноеВыражение" операция Разобрать определяет базу рекурсии. "АбстрактноеВыражение" определяет абстрактную операцию Разобрать, общую для всех узлов в абстрактном синтаксическом дереве. "Контекст" содержит информацию, глобальную по отношению к интерпретатору.
C#
[править | править код]using System;
using System.Collections;
namespace DoFactory.GangOfFour.Interpreter.Structural
{
class Program
{
static void Main()
{
var context = new Context();
var input = new MyExpression();
var expression = new OrExpression
{
Left = new EqualsExpression
{
Left = input,
Right = new MyExpression { Value = "4" }
},
Right = new EqualsExpression
{
Left = input,
Right = new MyExpression { Value = "четыре" }
}
};
// Output: true
input.Value = "четыре";
expression.Interpret(context);
Console.WriteLine(context.Result.Pop());
// Output: false
input.Value = "44";
expression.Interpret(context);
Console.WriteLine(context.Result.Pop());
}
}
class Context
{
public Stack<string> Result = new Stack<string>();
}
interface Expression
{
void Interpret(Context context);
}
abstract class OperatorExpression : Expression
{
public Expression Left { private get; set; }
public Expression Right { private get; set; }
public void Interpret(Context context)
{
Left.Interpret(context);
string leftValue = context.Result.Pop();
Right.Interpret(context);
string rightValue = context.Result.Pop();
DoInterpret(context, leftValue, rightValue);
}
protected abstract void DoInterpret(Context context, string leftValue, string rightValue);
}
class EqualsExpression : OperatorExpression
{
protected override void DoInterpret(Context context, string leftValue, string rightValue)
{
context.Result.Push(leftValue == rightValue ? "true" : "false");
}
}
class OrExpression : OperatorExpression
{
protected override void DoInterpret(Context context, string leftValue, string rightValue)
{
context.Result.Push(leftValue == "true" || rightValue == "true" ? "true" : "false");
}
}
class MyExpression : Expression
{
public string Value { private get; set; }
public void Interpret(Context context)
{
context.Result.Push(Value);
}
}
}
__doc__ = '''
Система для вычисления и манипулирования булевыми выражениями. Пример из книги
Gang of Four - "Design Patterns: Elements of Reusable Object-Oriented Software"
'''
from abc import ABCMeta, abstractmethod
class Context:
"""
Контекст среды исполнения интерпретатора
"""
def __init__(self, variables: dict = {}) -> None:
"""
Constructor.
:param variables: словарь соответствий имен переменных их значениям
"""
self._variables = variables
class ContextException(Exception):
"""
Исключение, прокидываемое в случае некорректной работы с данным классом
"""
pass
def lookup(self, name: str) -> bool:
"""
Получает значение переменной по ее имени
:param name: имя переменной
"""
if name in self._variables:
return self._variables[name]
raise self.ContextException('Неизвестная переменная {}'.format(name))
def assign(self, name: str, value: bool) -> None:
"""
Назначает значение переменной по ее имени
:param name: имя переменной
:param value: значение переменной
"""
self._variables[name] = value
class BooleanExp(metaclass=ABCMeta):
"""
Абстрактное логическое выражение
"""
@abstractmethod
def evaluate(self, context: Context) -> bool:
"""
Получение результата логического выражения
"""
pass
class ConstantExp(BooleanExp):
"""
Логическая константа
"""
def __init__(self, value: bool):
"""
Constructor.
:param value: значение выражения (True или False)
"""
self._value = value
def evaluate(self, context: Context):
return self._value
class VariableExp(BooleanExp):
"""
Логическая переменная (значение переменных хранится в объекте контекста интерпретатора)
"""
def __init__(self, name: str) -> None:
"""
Constructor.
:param name: название переменной
"""
self._name = name
def evaluate(self, context: Context) -> bool:
return context.lookup(self._name)
class BinaryOperationExp(BooleanExp, metaclass=ABCMeta):
"""
Абстрактный класс для бинарных логических операций
"""
def __init__(self, left: BooleanExp, right: BooleanExp) -> None:
"""
Constructor.
:param left: левый операнд
:param right: правый операнд
"""
self._left = left
self._right = right
class AndExp(BinaryOperationExp):
"""
Конъюнкция
"""
def evaluate(self, context: Context) -> bool:
return self._left.evaluate(context) and self._right.evaluate(context)
class OrExp(BinaryOperationExp):
"""
Дизъюнкция
"""
def evaluate(self, context: Context) -> bool:
return self._left.evaluate(context) or self._right.evaluate(context)
class NotExp(BooleanExp):
"""
Отрицание
"""
def __init__(self, operand: BooleanExp) -> None:
"""
Constructor.
:param operand: операнд, к которому применяется операция
"""
self._operand = operand
def evaluate(self, context: Context) -> bool:
return not self._operand.evaluate(context)
def execute_test(context: Context, x: bool, y: bool) -> None:
"""
Функция для выполнения тестирования нашего интерпретатора
"""
context.assign('x', x)
context.assign('y', y)
expression = OrExp( # (True and x) or (у and (not x))
AndExp(ConstantExp(True), VariableExp('x')),
AndExp(VariableExp('y'), NotExp(VariableExp('x')))
)
print(expression.evaluate(context))
if __name__ == '__main__':
print('OUTPUT:')
context = Context()
execute_test(context, True, False)
execute_test(context, False, True)
execute_test(context, False, False)
'''
OUTPUT:
True
True
False
'''
<?php
/**
* Пример шаблона Interpreter с использованием композиции
*/
abstract class Expression {
private static $_count = 0;
private $_key = null;
public abstract function interpret( InterpreterContext $context );
public function getKey() {
if( !isset( $this->_key ) ) {
self::$_count++;
$this->_key = self::$_count;
}
return $this->_key;
}
}
class LiteralExpression extends Expression {
private $_value = null;
public function __construct( $value ) {
$this->_value = $value;
}
public function interpret( InterpreterContext $context ) {
$context->replace( $this, $this->_value );
}
}
class VariableExpression extends Expression {
private $_name = null;
private $_val = null;
public function __construct( $name, $val = null ) {
$this->_name = $name;
$this->_val = $val;
}
public function interpret( InterpreterContext $context ) {
if( !is_null( $this->_val ) )
$context->replace( $this, $this->_val );
}
public function setValue( $value ) {
$this->_val = $value;
}
public function getKey() {
return $this->_name;
}
}
abstract class OperatorExpression extends Expression {
protected $leftoperand = null;
protected $rightoperand = null;
public function __construct( Expression $leftoperand, Expression $rightoperand ) {
$this->leftoperand = $leftoperand;
$this->rightoperand = $rightoperand;
}
public function interpret( InterpreterContext $context ) {
$this->leftoperand->interpret( $context );
$this->rightoperand->interpret( $context );
$resultleft = $context->lookup( $this->leftoperand );
$resultright = $context->lookup( $this->rightoperand );
$this->doInterpret( $context, $resultleft, $resultright );
}
protected abstract function doInterpret( InterpreterContext $context, $resultleft, $resultright );
}
class EqualsExpression extends OperatorExpression {
protected function doInterpret( InterpreterContext $context, $resultleft, $resultright ) {
$context->replace( $this, $resultleft == $resultright );
}
}
class BooleanOrExpression extends OperatorExpression {
protected function doInterpret( InterpreterContext $context, $resultleft, $resultright ) {
$context->replace( $this, $resultleft || $resultright );
}
}
class BooleanAndExpression extends OperatorExpression {
protected function doInterpret( InterpreterContext $context, $resultleft, $resultright ) {
$context->replace( $this, $resultleft && $resultright );
}
}
class InterpreterContext {
private $_expressionstore = array();
public function replace( Expression $exp, $value ) {
$this->_expressionstore[$exp->getKey()] = $value;
}
public function lookup( Expression $exp ) {
return $this->_expressionstore[$exp->getKey()];
}
}
$context = new InterpreterContext();
$input = new VariableExpression( 'input' );
$statement = new BooleanOrExpression(
new EqualsExpression( $input, new LiteralExpression( "четыре" ) ),
new EqualsExpression( $input, new LiteralExpression( "4" ) )
);
foreach( array( "четыре", "4", "52" ) as $value ) {
$input->setValue( $value );
print "{$value}:<br>";
$statement->interpret( $context );
print $context->lookup( $statement )
? "Соответствует<br><br>"
: "Не соответствует<br><br>";
}
?>