- •А.А. Волосевич
- •3. Шаблоны и архитектура программ
- •3.1. Модульное тестирование
- •3.2. Шаблоны проектирования
- •3.3. Структурные шаблоны: Декоратор, Заместитель, мост Декоратор (Decorator)
- •Заместитель (Proxy)
- •Мост (Bridge)
- •3.4. Структурные шаблоны: компоновщик и приспособленец Компоновщик (Composite)
- •Приспособленец (Flyweight)
- •3.5. Структурные шаблоны: адаптер и фасад Адаптер (Adapter)
- •Фасад (Façade)
- •3.6. Порождающие шаблоны: прототип, фабричный метод, одиночка Прототип (Prototype)
- •Фабричный метод (Factory method)
- •Одиночка (Singleton)
- •3.7. Порождающие шаблоны: абстрактная фабрика и строитель Абстрактная фабрика (Abstract factory)
- •Строитель (Builder)
- •3.8. Шаблоны поведения: стратегия, состояние, шаблонный метод Стратегия (Strategy)
- •Состояние (State)
- •Шаблонный метод (Template method)
- •3.9. Шаблоны поведения: цепочка обязанностей и команда Цепочка обязанностей (Chain of responsibility)
- •Команда (Command)
- •3.10. Шаблоны поведения: итератор, посредник, наблюдатель Итератор (Iterator)
- •Посредник (Mediator)
- •Наблюдатель (Observer)
- •3.11. Шаблоны поведения: посетитель, интерпретатор, хранитель Посетитель (Visitor)
- •Интерпретатор (Interpreter)
- •Хранитель (Memento)
- •3.12. Некоторые неклассические шаблоны проектирования
- •Неизменный объект (Immutable object)
- •Пул объектов (Object pool)
- •Отложенная инициализация (Lazy initialization)
- •Нулевой объект (Null object)
- •3.13. Антипаттерны
- •3.14. Архитектура прогРаммного Обеспечения
- •«Клиент-сервер»
- •Архитектура, основанная на использовании компонентов
- •Многоуровневая архитектура
- •Шина сообщений
- •Выделенное представление
- •Объектно-ориентированная архитектура
- •Архитектура, ориентированная на сервисы
Интерпретатор (Interpreter)
Шаблон Интерпретатор задаёт способ вычисления выражений, записанных на некотором специальном языке. Основная идея шаблона – определение класса для каждого символа языка. Вследствие этого выражение на языке представляется в виде композитной объектной структуры.
Сделаем небольшое отступление и рассмотрим формальные понятия язык и грамматика. Словарём будем называть выделенное конечное множество элементов. Элементы словаря будем называть символами, а последовательности символов – предложениями. Множество предложений назовём языком. Грамматикой называется любой конечный механизм задания языка. Формальная грамматика – разновидность грамматики, в которой язык задаётся при помощи порождающих правил. Каждое такое правило имеет вид «левая часть» → «правая часть», где «левая часть» – это непустая последовательность терминалов и нетерминалов, содержащая хотя бы один нетерминал, а «правая часть» – любая последовательность терминалов и нетерминалов. В свою очередь, понятие терминал означает объект, непосредственно присутствующий в словах языка, соответствующего грамматике, и имеющий конкретное, неизменяемое значение (обобщение понятия «буквы»). Нетерминал – это объект, обозначающий какую-либо сущность языка (например: формула, арифметическое выражение, команда) и не имеющий конкретного символьного значения.
Для компактного задания формальной грамматики часто используется форма Бакуса-Наура (БНФ). Вот, например, задание в БНФ грамматики, соответствующей обратной польской нотации для арифметических выражений:
expression ::= plus | minus | variable
plus ::= expression expression '+'
minus ::= expression expression '-'
variable ::= 'a' | 'b' | 'c' | ... | 'z'
Вернёмся к рассмотрению шаблона Интерпретатор. Структура шаблона представлена на рис. 23. Для каждого символа (терминала и нетерминала) создаётся отдельный класс, наследуемый от общего предка. Нетерминальные классы по определению агрегируют другие объекты-символы. Каждый из классов имеет метод, ответственный за вычисление выражения, соответствующего объекту класса.
Рис. 23. Структура шаблона Интерпретатор.
Рассмотрим пример реализации шаблона Интерпретатор. Опишем этот пример для грамматики, которая была представлена выше в виде БНФ. Следуя шаблону, определим классы для правил грамматики.
public interface Expression
{
int Interpret(IDictionary<string, int> variables);
}
public class Number : Expression
{
private readonly int number;
public Number(int number)
{
this.number = number;
}
public int Interpret(IDictionary<string, int> variables)
{
return number;
}
}
public class Plus : Expression
{
private readonly Expression leftOperand;
private readonly Expression rightOperand;
public Plus(Expression left, Expression right)
{
leftOperand = left;
rightOperand = right;
}
public int Interpret(IDictionary<string, int> variables)
{
return leftOperand.Interpret(variables) +
rightOperand.Interpret(variables);
}
}
public class Minus : Expression
{
private readonly Expression leftOperand;
private readonly Expression rightOperand;
public Minus(Expression left, Expression right)
{
leftOperand = left;
rightOperand = right;
}
public int Interpret(IDictionary<string, int> variables)
{
return leftOperand.Interpret(variables) –
rightOperand.Interpret(variables);
}
}
public class Variable : Expression
{
private readonly string name;
public Variable(string name)
{
this.name = name;
}
public int Interpret(IDictionary<string, int> variables)
{
return variables.ContainsKey(name) ? variables[name] : 0;
}
}
Хотя разбор строкового выражения и построение объектной структуры не является часть шаблона Интерпретатор, представим отдельный класс, выполняющий эти задачи.
public class Evaluator
{
private readonly Expression syntaxTree;
public Evaluator(string expression)
{
var expressionStack = new Stack<Expression>();
foreach (var token in expression.Split(' '))
{
switch (token)
{
case "+":
var plus = new Plus(expressionStack.Pop(),
expressionStack.Pop());
expressionStack.Push(plus);
break;
case "-":
var minus = new Minus(expressionStack.Pop(),
expressionStack.Pop());
expressionStack.Push(minus);
break;
default:
expressionStack.Push(new Variable(token));
break;
}
}
syntaxTree = expressionStack.Pop();
}
public int Evaluate(IDictionary<string, int> context)
{
return syntaxTree.Interpret(context);
}
}
Представим код, вычисляющий выражение "w x z - +", при w=5, x=10, z=4.
public class InterpreterExample
{
private static void Main()
{
var sentence = new Evaluator("w x z - +");
var vars = new Dictionary<string, int> { { "w", 5 },
{ "x", 10 }, { "z", 4 } };
Console.WriteLine(sentence.Evaluate(vars));
}
}