## Modern C++ Design Pattern/Chatper 14. 커맨드

created : 2020-04-19T12:40:23+00:00
modified : 2020-09-26T14:24:20+00:00

cpp design pattern command pattern

## 필요성

• 어떤 객체를 활용할 때 직접 그 객체의 API를 호출하여 조작하는 대신, 작업을 어떻게 하라고 명령을 보내는 방식을 제안한다.

## 시나리오

struct BankAccount
{
int balance = 0;
int overdraft_limit = -500;

void deposit(int amount)
{
balance += amount;
cout << "deposited " << amount << ", balance is now " << balance << "\n";
}

void withdraw(int amount)
{
if (balance - amount >= overdraft_limit)
{
balance -= amount;
cout << "withdrew " << amount << ", balance is now " << balance << "\n";
}
}
};


## 커맨드 패턴의 구현

struct Command
{
virtual void call() const = 0;
};

struct BankAccountCommand : Command
{
BankAccount& account;
enum Action { deposit, withdraw } action;
int amount;

BankAccountCommand(BankAccount& account, const Action action, const int amount)
: account(account), action(action), amount(amount) {}

void call() const override
{
switch (action)
{
case deposit:
account.deposit(amount);
break;
case withdraw:
account.withdraw(amount);
break;
}
}
};

/*
BankAccount ba;
BankAccountCommand cmd{ba, BankAccountCommand::deposit, 100};
cmd.call();
*/


## 되돌리기(Undo) 작업

struct BankAccount
{
bool widthdraw(int amount)
{
if (balance - amount >= overdraft_limit)
{
balance -= amount;
cout << "widthdrew " << amount << ", balance now " << balance << "\n";
return true;
}
return flase;
}
};
struct Command
{
virtual void call() const = 0;
virtual void undo() const = 0;
};

struct BankAccountCommand : Command
{
BankAccount& account;
enum Action { deposit, withdraw } action;
int amount;
bool widthdrawal_succeeded;

BankAccountCommand(BankAccount& account, const Action action, const int amount)
: account(account), action(action), amount(amount), widthdrawal_succeeded{false} {}

void call() const override
{
switch (action)
{
case deposit:
account.deposit(amount);
break;
case withdraw:
widthdrawal_succeeded = account.withdraw(amount);
break;
}
}

void undo() const override
{
switch (action)
{
case widthdraw:
if (widthdrawal_succeeded)
account.deposit(amount);
break;
case deposit:
account.widthdraw(amount);
break;
}
}
};

/*
BankAccount ba;
BankAccountCommand cmd{ba, BankAccountCommand::deposit, 100};
cmd.call();
*/


## 컴포지트 커맨드

struct CompositeBankAccountCommand : vector<BankAccountCommand>, Command
{
CompositeBankAccountCommand(const initializer_list<value_type>& items)
: vector<BankAccountCommand>(items) {}

void call() override
{
for (auto& cmd : *this)
cmd.call();
}

void undo() override
{
for (auto it = rbegin(); it != rend(); ++it)
it->undo();
}
}

struct Command {
bool succeeded;
}


## 명령과 조회의 분리

• Command Query Separation (CQS)

## 요약

• 작업 지시 내용을 감싸는 특별한 객체를 두어 객체와 커뮤니케이션 하게 한다.