- •Гагарина. Л.Г., Федоров а.Р., Федоров п.А. Введение в архитектуру проектирования программного обеспечения Москва
- •Оглавление
- •5.11. Паттерны grasp
- •Глава 1. Архитектура как форма концептуального существования по
- •1.1. Определения архитектуры по и ее значимость
- •1.2. Место архитектурных решений
- •1.3. Роль архитектурных решений
- •1.4. Архитектурные концептуальные схемы. Определение и ретроспектива
- •Глава 2. Нормативные практики архитектурного описания по
- •2.1. Основные понятия
- •2.2. Содержание стандарта
- •2.3. Представления схемы ieee-1471
- •Глава 3. Рациональный процесс архитектурного моделирования
- •3.1. Архитектурные парадигмы
- •3.2. Примеры архитектурных стилей и моделей
- •Глава 4. Сравнительное сопоставление архитектурных видов
- •4.1. Сопоставление систем видов
- •4.2. Примеры систем видов
- •Языки описания архитектуры
- •Глава 5. Проектирование с учетом будущих изменений.
- •5.1. Что такое паттерн проектирования.
- •5.2. Как решать задачи проектирования с помощью паттернов
- •3. Зависимость от аппаратной и программной платформ.
- •5.4. Порождающие паттерны
- •5.4.1. Паттерн Фабричный метод (Factory Method) - уровень класса
- •Классический вариант фабричного метода, когда интерфейс фабричных методов объявляется в независимом классе-фабрике, а их реализация определяется конкретными подклассами этого класса (Рис. 33).
- •Реализация паттерна Factory Method на основе обобщенного конструктора
- •Void info() {
- •Void info() {
- •Void info() {
- •Int main()
- •V.Push_back( Warrior::createWarrior( Infantryman_id));
- •V.Push_back( Warrior::createWarrior( Archer_id));
- •V.Push_back( Warrior::createWarrior( Horseman_id));
- •Void info() {
- •Void info() {
- •Void info() {
- •Int main()
- •5.4.2. Паттерн Одиночка (Singleton) - уровень объекта
- •Классическая реализация Singleton
- •If(!p_instance)
- •Singleton Мэйерса
- •Улучшенная версия классической реализации Singleton
- •Void initialize( Singleton* p );
- •Void SingletonDestroyer::initialize( Singleton* p ) {
- •If(!p_instance) {
- •Использование нескольких взаимозависимых одиночек
- •Int main()
- •5.4.3. Паттерн Абстрактная фабрика (Abstract Factory) - уровень объекта
- •Пример кода для паттерна Abstract Factory
- •Void info() {
- •Int main()
- •5.4.4. Паттерн Строитель (Builder) - уровень объекта
- •Void info() {
- •Int main()
- •Infantryman
- •Infantryman
- •5.4.5. Паттерн Прототип (Prototype) - уровень объекта
- •Void info() {
- •Void info() {
- •5.4.6. Обсуждение порождающих паттернов
- •5.5. Структурные паттерны
- •5.5.1. Паттерн Адаптер, обертка (Adapter, wrapper)
- •Адаптер объекта применяет композицию объектов.
- •Int main()
- •Void adjust() {} // Настройка датчика (защищенный метод)
- •5.5.2. Паттерн Мост (Bridge)
- •Void log( string & str );
- •Достоинства паттерна Bridge
- •5.5.3. Паттерн компоновщик (Composite)
- •Описание паттерна Composite
- •Virtual void addUnit(Unit* p) {
- •Int main()
- •Virtual CompositeUnit* getComposite() {
- •Void addUnit(Unit* p);
- •Достоинства паттерна Composite
- •Недостатки паттерна Composite
- •5.5.4. Паттерн декоратор (Decorator , wrapper, обертка)
- •Реализация паттерна Decorator
- •Int width, height;
- •5.5.5. Паттерн фасад (Facade)
- •Void submitNetworkRequest()
- •If (_engineer.CheckOnStatus())
- •5.5.6. Паттерн приспособленец (Flyweight)
- •Структура паттерна приспособленец (Flyweight)
- •Icon(char *fileName)
- •Icon *FlyweightFactory::_icons[];
- •5.5.7 Паттерн заместитель (Proxy, surrogate, суррогат)
- •Void draw()
- •Int balance;
- •5.5.8. Обсуждение структурных паттернов
- •5.6. Паттерны поведения
- •5.6.1. ПаттернЦепочка обязанностей (Chain of Responsibility)
- •Void setNext(Base *n)
- •5.6.2. Паттерн Command (команда)
- •Реализация паттерна Command
- •5.6.3. Паттерн Interpreter (интерпетатор)
- •Совместное использование паттернов Interpreter и Template Method
- •Int interpret(char*); // interpret() for client
- •Virtual void interpret(char *input, int &total)
- •Int index;
- •If (!strncmp(input, nine(), 2))
- •Virtual char one(){}
- •Int items[10];
- •Int main()
- •5.6.5. Паттерн Mediator (посредник)
- •Virtual void changed();
- •Void Widget::changed()
- •Int main()
- •5.6.6. Паттерн Memento (хранитель)
- •Int _state;
- •Void static redo()
- •Int main()
- •Integer: 11
- •5.6.7. Паттерн Observer (наблюдатель)
- •Int value;
- •5.6.8. Паттерн State
- •Void setCurrent(State *s)
- •5.6.9. Паттерн Strategy
- •Void compress( const string & file ) {
- •5.6.10. Паттерн Template Method (шаблонный метод)
- •Void a()
- •V.Visit(this);
- •V.Visit(this);
- •V.Visit(this);
- •Int main()
- •5.6.12. Обсуждение паттернов поведения
- •5.7. Дальнейшее развитие идеи паттернов проектирования
- •5.8. Архитектурные системные паттерны
- •5.9. Паттерны управления
- •5.9.1. Паттерны централизованного управления
- •5.10. Паттерны интеграции корпоративных информационных систем
- •5.10.1. Структурные паттерны интеграции
- •5.10.2. Паттерны по методу интеграции
- •5.10.3. Паттерны интеграции по типу обмена данными
- •5.12. Антипаттерны (anti-patterns)
- •Глава 6. Архитектура и характеристики качества
- •6.1. Специфика требований к качеству по
- •6.2. Подход к построению архитектуры с позиций качества
- •6.3. Подходы к оцениванию архитектуры
Совместное использование паттернов Interpreter и Template Method
Рассмотрим задачу интерпретирования (вычисления) значений строковых представлений римских чисел. Используем следующую грамматику.
romanNumeral ::= {thousands} {hundreds} {tens} {ones}
thousands,hundreds,tens,ones ::= nine | four | {five} {one} {one} {one}
nine ::= "CM" | "XC" | "IX"
four ::= "CD" | "XL" | "IV"
five ::= 'D' | 'L' | 'V'
one ::= 'M' | 'C' | 'X' | 'I'
Для проверки и интерпретации строки используется иерархия классов с общим базовым классом RNInterpreter, имеющим 4 под-интерпретатора. Каждый под-интерпретатор получает "контекст" (оставшуюся неразобранную часть строки и накопленное вычисленное значение разобранной части) и вносит свой вклад в процесс обработки. Под-переводчики просто определяют шаблонные методы, объявленные в базовом классе RNInterpreter.
#include <iostream.h>
#include <string.h>
class Thousand;
class Hundred;
class Ten;
class One;
class RNInterpreter
{
public:
RNInterpreter(); // ctor for client
RNInterpreter(int){}
// ctor for subclasses, avoids infinite loop
Int interpret(char*); // interpret() for client
Virtual void interpret(char *input, int &total)
{
// for internal use
Int index;
index = 0;
If (!strncmp(input, nine(), 2))
{
total += 9 * multiplier();
index += 2;
}
else if (!strncmp(input, four(), 2))
{
total += 4 * multiplier();
index += 2;
}
else
{
if (input[0] == five())
{
total += 5 * multiplier();
index = 1;
}
else
index = 0;
for (int end = index + 3; index < end; index++)
if (input[index] == one())
total += 1 * multiplier();
else
break;
}
strcpy(input, &(input[index]));
} // remove leading chars processed
protected:
// cannot be pure virtual because client asks for instance
Virtual char one(){}
virtual char *four(){}
virtual char five(){}
virtual char *nine(){}
virtual int multiplier(){}
private:
RNInterpreter *thousands;
RNInterpreter *hundreds;
RNInterpreter *tens;
RNInterpreter *ones;
};
class Thousand: public RNInterpreter
{
public:
// provide 1-arg ctor to avoid infinite loop in base class ctor
Thousand(int): RNInterpreter(1){}
protected:
char one()
{
return 'M';
}
char *four()
{
return "";
}
char five()
{
return '\0';
}
char *nine()
{
return "";
}
int multiplier()
{
return 1000;
}
};
class Hundred: public RNInterpreter
{
public:
Hundred(int): RNInterpreter(1){}
protected:
char one()
{
return 'C';
}
char *four()
{
return "CD";
}
char five()
{
return 'D';
}
char *nine()
{
return "CM";
}
int multiplier()
{
return 100;
}
};
class Ten: public RNInterpreter
{
public:
Ten(int): RNInterpreter(1){}
protected:
char one()
{
return 'X';
}
char *four()
{
return "XL";
}
char five()
{
return 'L';
}
char *nine()
{
return "XC";
}
int multiplier()
{
return 10;
}
};
class One: public RNInterpreter
{
public:
One(int): RNInterpreter(1){}
protected:
char one()
{
return 'I';
}
char *four()
{
return "IV";
}
char five()
{
return 'V';
}
char *nine()
{
return "IX";
}
int multiplier()
{
return 1;
}
};
RNInterpreter::RNInterpreter()
{
// use 1-arg ctor to avoid infinite loop
thousands = new Thousand(1);
hundreds = new Hundred(1);
tens = new Ten(1);
ones = new One(1);
}
int RNInterpreter::interpret(char *input)
{
int total;
total = 0;
thousands->interpret(input, total);
hundreds->interpret(input, total);
tens->interpret(input, total);
ones->interpret(input, total);
if (strcmp(input, ""))
// if input was invalid, return 0
return 0;
return total;
}
int main()
{
RNInterpreter interpreter;
char input[20];
cout << "Enter Roman Numeral: ";
while (cin >> input)
{
cout << " interpretation is "
<< interpreter.interpret(input) << endl;
cout << "Enter Roman Numeral: ";
}
}
Вывод программы:
Enter Roman Numeral: MCMXCVI
interpretation is 1996
Enter Roman Numeral: MMMCMXCIX
interpretation is 3999
Enter Roman Numeral: MMMM
interpretation is 0
Enter Roman Numeral: MDCLXVIIII
interpretation is 0
Enter Roman Numeral: CXCX
interpretation is 0
Enter Roman Numeral: MDCLXVI
interpretation is 1666
Enter Roman Numeral: DCCCLXXXVIII
interpretation is 888
Родственные паттерны
Компоновщик: абстрактное синтаксическое дерево - это пример применения паттерна компоновщик.
Приспособленец показывает варианты разделения терминальных символов в абстрактном синтаксическом дереве.
Итератор: интерпретатор может пользоваться итератором для обхода структуры.
Посетителя можно использовать для инкапсуляции в одном классе поведения каждого узла абстрактного синтаксического дерева.
5.6.4. Паттерн Iterator (итератор, cursor, курсор)
Название и классификация паттерна
Итератор - паттерн поведения объектов.
Назначение
Предоставляет способ последовательного доступа ко всем элементам составного объекта, не раскрывая его внутреннего представления.
Абстракция в стандартных библиотеках C++ и Java, позволяющая разделить классы коллекций и алгоритмов.
Придает обходу коллекции "объектно-ориентированный статус".
Полиморфный обход.
Решаемая проблема
Вам необходим механизм "абстрактного" обхода различных структур данных так, что могут определяться алгоритмы, способные взаимодействовать со структурами прозрачно.
Обсуждение паттерна Iterator
Составной объект, такой как список, должен предоставлять способ доступа к его элементам без раскрытия своей внутренней структуры. Более того, иногда нужно перебирать элементы списка различными способами, в зависимости от конкретной задачи. Но вы, вероятно, не хотите усложнять интерфейс списка операциями для различных обходов, даже если они необходимы. Кроме того, иногда нужно иметь несколько активных вариантов обхода одного списка одновременно. Было бы хорошо иметь единый интерфейс для обхода разных типов составных объектов (т.е. полиморфная итерация).
Паттерн Iterator позволяет все это сделать. Ключевая идея состоит в том, чтобы за доступ к элементам и способ обхода отвечал не сам список, а отдельный объект итератор, который и будет определять необходимый протокол обхода.
В классе Iterator определен интерфейс для доступа к элементам списка. Объект этого класса отслеживает текущий элемент, то есть он располагает информацией, какие элементы уже посещались. Например, класс List мог бы предусмотреть класс Listlterator.

Прежде чем создавать экземпляр класса Listlterator, необходимо иметь список, подлежащий обходу. С объектом Listlterator вы можете последовательно посетить все элементы списка. Операция CurrentItem возвращает текущий элемент списка, операция First инициализирует текущий элемент первым элементом списка, Next делает текущим следующий элемент, a IsDone проверяет, не оказались ли мы за последним элементом, если да, то обход завершен.
Абстракция Iterator имеет основополагающее значение для технологии, называемой "обобщенное программирование". Эта технология четко разделяет такие понятия как "алгоритм" и "структура данных". Мотивирующие факторы: способствование компонентной разработке, повышение производительности и снижение расходов на управление.
Применимость
Использоване паттерна итератор оправдано:
для доступа к содержимому агрегированных объектов без раскрытия их внутреннего представления;
для поддержки нескольких активных обходов одного и того же агрегированного объекта;
для предоставления единообразного интерфейса с целью обхода различных агрегированных структур (то есть для поддержки полиморфной итерации).
Структура паттерна Iterator
Для манипулирования коллекцией клиент использует открытый интерфейс класса Collection. Однако доступ к элементам коллекции инкапсулируется дополнительным уровнем абстракции, называемым Iterator. Каждый производный от Collection класс знает, какой производный от Iterator класс нужно создавать и возвращать. После этого клиент использует интерфейс, определенный в базовом классе Iterator.
UML-диаграмма классов паттерна Iterator

Участники
Iterator - итератор: определяет интерфейс для доступа и обхода элементов;
Concretelterator - конкретный итератор:
- реализует интерфейс класса Iterator;
- следит за текущей позицией при обходе агрегата;
Aggregate - агрегат: определяет интерфейс для создания объекта-итератора;
ConcreteAggregate - конкретный агрегат: реализует интерфейс создания итератора и возвращает экземпляр подходящего класса Concretelterator.
Результаты
У паттерна итератор есть следующие важные особенности:
поддерживает различные виды обхода агрегата. Сложные агрегаты можно обходить по-разному. Генератор кода может обходить дерево во внутреннем или прямом порядке. Итераторы упрощают изменение алгоритма обхода - достаточно просто заменить один экземпляр итератора другим. Для поддержки новых видов обхода можно определить и подклассы класса Iterator;
итераторы упрощают интерфейс класса Aggregate. Наличие интерфейса для обхода в классе Iterator делает излишним дублирование этого интерфейса в классе Aggregate;
одновременно для данного агрегата может быть активно несколько обходов. Итератор следит за инкапсулированным в нем самом состоянием обхода. Поэтому одновременно разрешается осуществлять несколько обходов агрегата.
Пример паттерна Iterator
В офисных условиях, когда доступ к документам осуществляется через секретаря, именно секретарь выступает в качестве Iterator. Для руководителя система регистрации может казаться запутанной и нелогичной, однако секретарь может быстро и эффективно найти нужный документ.
![]()
Реализация паттерна Iterator
Каждый контейнерный класс должен иметь итератор. Может показаться, что это является нарушением принципа инкапсуляции, так как пользователи класса Stack получают доступ к его содержимому напрямую. Однако при проектировании класса неизбежно что-то будет упущено. Позже, когда потребуется внести дополнительную функциональность, если итератор первоначально был предусмотрен, то можно будет добавить эту функциональность в соответствии с принципом "открыт для расширения, закрыт для модификации".
В приведенном ниже примере исходный класс Stack не содержит оператор равенства, но имеет итератор. В результате, оператор равенства может быть легко добавлен.
#include <iostream>
using namespace std;
class Stack
{
