Рассмотрим реализацию паттерна Command на примере игры «Шахматы». Имитируем возможность выполнения следующих операций:
· Создать новую игру.
· Открыть существующую игру.
· Сохранить игру.
· Сделать очередной ход.
· Отменить последний ход.
#include<iostream> #include<vector> #include<string> class Game { public: void create() { cout << "Create game " << endl; } void open(string file) { cout << "Open game from " << file << endl; } void save(string file) { cout << "Save game in " << file << endl; } void make_move(string move) { cout << "Make move " << move << endl; } }; string getPlayerInput(string prompt) { string input; cout << prompt; cin >> input; return input; } // Базовый класс class Command { public: virtual ~Command() {} virtual void execute() = 0; protected: Command(Game* p): pgame(p) {} Game * pgame; }; class CreateGameCommand: public Command { public: CreateGameCommand(Game * p): Command(p) {} void execute() { pgame->create(); } }; class OpenGameCommand: public Command { public: OpenGameCommand(Game * p): Command(p) {} void execute() { string file_name; file_name = getPlayerInput("Enter file name:"); pgame->open(file_name); } }; class SaveGameCommand: public Command { public: SaveGameCommand(Game * p): Command(p) {} void execute() { string file_name; file_name = getPlayerInput("Enter file name:"); pgame->save(file_name); } }; class MakeMoveCommand: public Command { public: MakeMoveCommand(Game * p): Command(p) {} void execute() { // Сохраним игру для возможного последующего отката pgame->save("TEMP_FILE"); string move; move = getPlayerInput("Enter your move:"); pgame->make_move(move); } }; class UndoCommand: public Command { public: UndoCommand(Game * p): Command(p) {} void execute() { // Восстановим игру из временного файла pgame->open("TEMP_FILE"); } }; int main() { Game game; // Имитация действий игрока vector<Command*> v; // Создаем новую игру v.push_back(new CreateGameCommand(&game)); // Делаем несколько ходов v.push_back(new MakeMoveCommand(&game)); v.push_back(new MakeMoveCommand(&game)); // Последний ход отменяем v.push_back(new UndoCommand(&game)); // Сохраняем игру v.push_back(new SaveGameCommand(&game)); for (size_t i=0; i<v.size(); ++i) v[i]->execute(); for (size_t i=0; i<v.size(); ++i) delete v[i]; return 0; } |
Вывод программы:
|
|
Create game Save game in TEMP_FILE Enter your move: E2-E4 Make move E2-E4 Save game in TEMP_FILE Enter your move: D2-D3 Make move D2-D3 Open game from TEMP_FILE Enter file name: game1.sav Save game in game1.sav |
Результаты применения паттерна Command
Достоинства паттерна Command
· Придает системе гибкость, отделяя инициатора запроса от его получателя.
Пример:
#include <iostream>
#include <vector>
#include <string>
using namespace std;
Class Document
{
vector<string> data;
public:
void Insert(int line, const string & str)
{
if (line <= data.size())
data.insert(data.begin() + line, str);
else
cout << "Error!" << endl;
}
void Remove(int line)
{
if(!(line>data.size()))
data.erase(data.begin() + line);
else
cout << "Error!" << endl;
}
string & operator [] (int x)
{
return data[x];
}
void Show()
{
for(int i = 0; i<data.size(); ++i)
{
cout << i + 1 << ". " << data[i] << endl;
}
}
};
Class Command
{
protected:
Document * doc;
public:
virtual void Execute() = 0;
virtual void unExecute() = 0;
void setDocument(Document * _doc)
{
doc = _doc;
}
};
Class InsertCommand: public Command
|
|
{
int line;
string str;
public:
InsertCommand(int _line, const string & _str): line(_line), str(_str) {}
void Execute()
{
doc->Insert(line, str);
}
void unExecute()
{
doc->Remove(line);
}
};
Class Receiver
{
vector<Command*> DoneCommands;
Document doc;
Command* command;
public:
void Insert(int line, string str)
{
command = new InsertCommand(line, str);
command->setDocument(&doc);
command->Execute();
DoneCommands.push_back(command);
}
void Undo()
{
if(DoneCommands.size() == 0)
{
cout << "There is nothing to undo!" << endl;
}
else
{
command = DoneCommands.back();
DoneCommands.pop_back();
command->unExecute();
// Don't forget to delete command!!!
delete command;
}
}
void Show()
{
doc.Show();
}
};
Int main()
{
char s = '1';
int line, line_b;
string str;
Receiver res;
while(s!= 'e')
{
cout << "What to do: \n1.Add a line\n2.Undo last command" << endl;
cin >> s;
switch(s)
{
case '1':
cout << "What line to insert: ";
cin >> line;
--line;
cout << "What to insert: ";
cin >> str;
res.Insert(line, str);
break;
case '2':
res.Undo();
break;
}
cout << "$$$DOCUMENT$$$" << endl;
res.Show();
cout << "$$$DOCUMENT$$$" << endl;
}
}
Задания для лабораторной работы
В каждом из вариантов указан шаблон для реализации и проект, использующий этот шаблон.
Необходимо сделать следующее:
1. Нарисовать в UML диаграмму классов реализуемой программы. (проектирование)
2. Реализовать программу на С++. (реализация)
Для каждого из шаблонов, предложенных в вариантах можно найти пример реализации UML и кода в приложенной книге “Паттерны проектирования”.
Также указана глава, где подробно описан данный шаблон.
Вариант №1, 9, 17, 25
Шаблон “Стратегия”. Проект “Принтеры”. В проекте должны быть реализованы разные модели принтеров, которые выполняют разные виды печати. Пример использования шаблона в главе 1.
Вариант №2, 10, 18, 26
Шаблон “Наблюдатель”. Проект “Оповещение постов ГАИ”. В проекте должна быть реализована отправка сообщений всем постам ГАИ. Пример использования шаблона в главе 2.
Вариант №3, 11, 19, 27
Шаблон “Декоратор”. Проект “Универсальная электронная карта”. В проекте должна быть реализована универсальная электронная карта, в которой есть функции паспорта, страхового полиса, банковской карты и т. д. Пример использования шаблона в главе 3.
Вариант №4, 12, 20, 28
Шаблон “Фабричный метод”. Проект “Фабрика смартфонов”. В проекте должно быть реализовано создание смартфонов с различными характеристиками. Пример использования шаблона в главе 4.
Вариант №5, 13, 21, 29
Шаблон “Абстрактная фабрика”. Проект “Заводы по производству автомобилей”. В проекте должно быть реализована возможность создавать автомобили различных типов на разных заводах.
Вариант №6, 14, 22, 30
Шаблон “Команда”. Проект “Клавиатура настраимаемого калькулятора”. Цифровые и арифметические кнопки имеют фиксированную функцию, а остальные могут менять своё назначение.
Вариант №7, 15, 23
Шаблон “Адаптер”. Проект “Часы”. В проекте должен быть реализован адаптер, который дает возможность пользоваться часами со стрелками так же, как и цифровыми часами. В классе “Часы со стрелками” хранятся повороты стрелок. Пример использования шаблона в главе 7.
Вариант №8, 16, 24
Шаблон “Фасад”. Проект “Компьютер”. В проекте должен быть реализован “компьютер”, который выполняет основные функции, к примеру, включение, выключение, запуск ОС, запуск программы, и т.д, не раскрывая клиенту деталей выполнения этой операции. Пример использования шаблона в главе 7.
Литература, ссылки
1. https://ru.wikipedia.org/wiki/%D8%E0%E1%EB%EE%ED_%EF%F0%EE%E5%EA%F2%E8%F0%
EE%E2%E0%ED%E8%FF
2. http://citforum.ru/SE/project/pattern/
3. Э.Фримен, К.Сьерра, Б.Бейтс Паттерны проектирования. СПб.:
Питер, 2011.
4. Классическая книга “банды четырех”. Э. Гамма, Р. Хелм, Р. Джонсон, Д. Влиссидес. Приемы объектно-ориентированного
проектирования. Паттерны проектирования. (разные издания,
последнее 2015).