#include <iostream>
#include <list>
using namespace std;
// "Receiver" class
// デバイスとか、出力するオブジェクトな感じ
class Display {
public:
void Action(double result, double oprnd_1st, double oprnd_2nd,
string operation)
{
cout << result << " (" << oprnd_1st << " "
<< operation << " " << oprnd_2nd << ")" << endl;
}
void UndoAction(double result) {
cout << "[undo] " << result << endl;
}
void RedoAction(double result, double oprnd_1st, double oprnd_2nd,
string operation)
{
cout << "[redo] " << result << " (" << oprnd_1st << " "
<< operation << " " << oprnd_2nd << ")" << endl;
}
};
// "Command" abstract class
class Command {
public:
Command(Display *disp, double operand) {
m_disp = disp;
m_operand = operand;
}
virtual double Execute(double cur_val) = 0;
virtual double Undo() = 0;
virtual double Redo() = 0;
protected:
Display *m_disp;
double m_org_val, m_operand, m_result;
};
// "ConcreteCommand" class
class PlusCommand : public Command {
public:
PlusCommand(Display *disp, double operand)
: Command(disp, operand) { }
double Execute(double cur_val) {
m_org_val = cur_val;
m_result = m_org_val + m_operand;
m_disp->Action(m_result, m_org_val, m_operand, "+");
return m_result;
}
double Undo() {
m_disp->UndoAction(m_org_val);
return m_org_val;
}
double Redo() {
m_disp->RedoAction(m_result, m_org_val, m_operand, "+");
return m_result;
}
};
// "ConcreteCommand" class
class MinusCommand : public Command {
public:
MinusCommand(Display *disp, double operand)
: Command(disp, operand) { }
double Execute(double cur_val) {
m_org_val = cur_val;
m_result = m_org_val - m_operand;
m_disp->Action(m_result, m_org_val, m_operand, "-");
return m_result;
}
double Undo() {
m_disp->UndoAction(m_org_val);
return m_org_val;
}
double Redo() {
m_disp->RedoAction(m_result, m_org_val, m_operand, "-");
return m_result;
}
};
// "ConcreteCommand" class
class MultiplicationCommand : public Command {
public:
MultiplicationCommand(Display *disp, double operand)
: Command(disp, operand) { }
double Execute(double cur_val) {
m_org_val = cur_val;
m_result = m_org_val * m_operand;
m_disp->Action(m_result, m_org_val, m_operand, "*");
return m_result;
}
double Undo() {
m_disp->UndoAction(m_org_val);
return m_org_val;
}
double Redo() {
m_disp->RedoAction(m_result, m_org_val, m_operand, "*");
return m_result;
}
};
// "ConcreteCommand" class
class DivisionCommand : public Command {
public:
DivisionCommand(Display *disp, double operand)
: Command(disp, operand) { }
double Execute(double cur_val) {
if (m_operand == 0) {
cout << "division error." << endl;
return cur_val;
} else {
m_org_val = cur_val;
m_result = m_org_val / m_operand;
m_disp->Action(m_result, m_org_val, m_operand, "/");
return m_result;
}
}
double Undo() {
m_disp->UndoAction(m_org_val);
return m_org_val;
}
double Redo() {
m_disp->RedoAction(m_result, m_org_val, m_operand, "/");
return m_result;
}
};
// "Invoker" class
class Calculator {
public:
Calculator(double init_val) {
m_cur_val = init_val;
cout << "initial value: " << m_cur_val << endl;
m_cur_cmd_it = m_history.begin();
}
~Calculator() {
list<Command*>::iterator it = m_history.begin();
while (it != m_history.end()) {
delete *it;
++it;
}
}
void Exec(Command *cmd) {
if (m_cur_cmd_it != m_history.begin()) {
// 戻した分を削除
m_history.erase(m_history.begin(), m_cur_cmd_it);
}
m_history.push_front(cmd); // 先頭に追加
m_cur_cmd_it = m_history.begin();
m_cur_val = cmd->Execute(m_cur_val);
}
void Undo(unsigned int num = 1) {
while (num--) {
if (m_cur_cmd_it == m_history.end()) {
cout << "could not undo." << endl;
break;
}
m_cur_val = (*m_cur_cmd_it)->Undo();
++m_cur_cmd_it;
}
}
void Redo(unsigned int num = 1) {
while (num--) {
if (m_cur_cmd_it == m_history.begin()) {
cout << "could not redo." << endl;
return;
}
--m_cur_cmd_it;
m_cur_val = (*m_cur_cmd_it)->Redo();
}
}
private:
double m_cur_val;
list<Command*> m_history;
list<Command*>::iterator m_cur_cmd_it;
};
// client
int main() {
// invoker
Calculator *calc = new Calculator(0);
// receiver
Display *disp = new Display();
calc->Exec(new PlusCommand(disp, 10)); // 10
calc->Exec(new PlusCommand(disp, 10)); // 20
calc->Exec(new MinusCommand(disp, 5)); // 15
calc->Exec(new MultiplicationCommand(disp, 3)); // 45
calc->Exec(new DivisionCommand(disp, 10)); // 4.5
calc->Undo(3); // 45 => 15 => 20
calc->Redo(2); // 15 => 45
calc->Exec(new PlusCommand(disp, 12)); // 57
calc->Exec(new MinusCommand(disp, 2)); // 55
calc->Exec(new DivisionCommand(disp, 10)); // 5.5
calc->Undo(); // 55
calc->Redo(2);
delete disp;
delete calc;
return 0;
}