반응형
"함수를 명령으로 바꾸기(Replace Function with Command)"는 함수를 객체로 대체하는 리팩터링 기법 중 하나입니다.
함수를 독립적인 명령 객체로 만드는 방법입니다.
이 리팩터링은 함수 호출을 객체의 메서드 호출로 바꿈으로써 코드의 유연성과 확장성을 향상시킵니다.
- 대부분의 명령 객체는 메서드 하나로 구성되며 이 메서드를 요청해 실행합니다.
- 명령 객체는 되돌리기(undo) 기능을 만들 수 있습니다.
- 더 복잡한 기능의 여러 메소드를 추가할 수 있습니다.
- 상속을 사용하여 사용자 맞춤형으로 만들 수 있습니다.
다음 예제는 은행 계좌(Account)에서 돈을 인출(withdraw)하는 상황을 나타냅니다.
//Account 클래스: 은행 계좌를 나타내는 클래스로, 계좌의 잔액(balance)을 포함하고 있습니다.
class Account
{
private:
int balance;
int overdraftLimit;
public:
Account(int balance, int overdraftLimit) :
balance(balance), overdraftLimit(overdraftLimit)
{
}
void withdraw(int amount) {
if (amount > balance + overdraftLimit) {
throw std::runtime_error("Insufficient funds");
}
balance -= amount;
}
int getBalance() const {
return balance;
}
};
//Command 클래스: 은행 계좌에서 실행되는 명령을 나타내는 클래스로,
//계좌에서 출금(withdraw) 명령을 수행하는 execute() 함수를 가지고 있습니다.
class Command {
public:
virtual ~Command() = default;
virtual void execute() = 0;
};
//WithdrawCommand 클래스: 출금 명령을 나타내는 클래스로,
//Command 클래스를 상속받아서 execute() 함수에서 실제 출금을 수행합니다.
class WithdrawCommand : public Command {
private:
Account& account;
int amount;
public:
WithdrawCommand(Account& account, int amount) :
account(account), amount(amount)
{
}
void execute() override {
account.withdraw(amount);
}
};
//Transaction 클래스는 Command 객체들을 가지고 있고,
//execute() 메서드를 호출하여 여러 개의 Command 객체를 실행할 수 있습니다.
//각 Command 객체는 은행 계좌에서 출금하는 작업을 수행합니다.
class Transaction {
private:
std::vector<std::unique_ptr<Command>> commands;
public:
void addCommand(std::unique_ptr<Command> command) {
commands.push_back(std::move(command));
}
void execute() {
for (auto& command : commands) {
command->execute();
}
}
};
위 코드에서 WithdrawCommand 클래스는 이전에 Account::withdraw 함수가 하던 일을 대신 수행합니다.
Transaction 클래스는 여러 개의 Command 객체를 저장하고 이를 실행하는 책임을 갖습니다.
Transaction 클래스를 사용하면, Account 객체의 withdraw 함수 호출을 Transaction 객체의 addCommand 함수 호출로 대체할 수 있습니다.
아래는 Replace Function with Command 리팩터링 기법을 적용하기 전 코드와 적용 후 코드의 사용 예시입니다.
리팩터링 적용 전:
int main() {
Account account(100, -50);
account.withdraw(75);
std::cout << "Balance: " << account.getBalance() << std::endl; // Output: 25
return 0;
}
리팩터링 적용 후:
int main() {
Account account(100, -50);
std::unique_ptr<Command> withdrawCommand = std::make_unique<WithdrawCommand>(account, 75);
Transaction transaction;
transaction.addCommand(std::move(withdrawCommand));
transaction.execute();
std::cout << "Balance: " << account.getBalance() << std::endl; // Output: 25
return 0;
}
리팩터링을 적용하면 코드의 유연성과 확장성이 향상됩니다.
Transaction 클래스를 사용하면 여러 개의 Command 객체를 저장하고 한꺼번에 실행할 수 있으므로, 나중에 새로운 Command 객체를 추가하거나 기존 Command 객체를 수정해도 Account 클래스의 코드를 변경하지 않아도 됩니다.
'리팩터링 > 리팩터링 기법' 카테고리의 다른 글
리팩터링. 반복문 쪼개기 (Split Loop) (0) | 2023.02.22 |
---|---|
리팩터링. 조건문 분해하기 (Decompose Conditional) (0) | 2023.02.21 |
리팩터링. 객체 통째로 넘기기 (Preserve Whole Object) (0) | 2023.02.19 |
리팩터링. 매개변수 객체 만들기 (Introduce Parameter Object) (0) | 2023.02.18 |
리팩터링 기법. 임시 변수를 질의 함수로 바꾸기 (Replace Temp with Query) (0) | 2023.02.17 |