Composite pattern과 같은 느낌.
composite가 재귀적 포함을 사용한 복합 객체를 만들었다면,
decorator는 재귀적 포함을 사용하여 객체에 기능을 추가한다.
얘를 들어, 게임을 하나 만든다고 하자.
나는 무기에 관한 것을 만들게 되었고, 각 무기에는 기본 데미지와 랜덤으로 붙을 수 있는 옵션들이 있다.
이런 느낌으로... 랜덤으로 잠재 옵션이 붙을 수 있게.
살짝 바꾸어서 잠재 옵션이 아닌 추가 데미지 옵션을 단다고 해보자.
일단 무기 클래스가 하나 있어야 할 것이다.
그 클래스에는 bonusDamage 옵션을 달 수 있게 할 것이고, 각각의 옵션을 class로 구현할 것이다.
기본 데미지를 주는 class를 만들어 두고, weapon class에서 damage를 얻을 수 있게 해 보자.
#include <iostream>
class IBonusDamage {
protected:
public:
IBonusDamage() {}};
virtual ~IBonusDamage() {};
virtual double getRealDamage(double baseDamage) = 0;
};
class BaseDamage : public IBonusDamage {
public:
BaseDamage() : IBonusDamage() {}
double getRealDamage(double baseDamage) override {
return baseDamage;
};
};
class Weapon {
double BaseDeal;
double getBaseDamage() {
return BaseDeal * 100;
}
public:
Weapon(double BaseDeal) : BaseDeal(BaseDeal) {
}
virtual ~Weapon() {}
double getAttackDamage() {
double TotalDamage = getBaseDamage();
return TotalDamage;
}
};
int main(){
Weapon* sword = new Weapon(100);
Weapon* staff = new Weapon(50);
Weapon* broken_sword = new Weapon(10);
std::cout << sword->getAttackDamage() << std::endl;
std::cout << staff->getAttackDamage() << std::endl;
std::cout << broken_sword->getAttackDamage() << std::endl;
}
이와 같은 방식으로 각 weapon을 통해 공격했을 때의 실제 데미지를 얻는 코드를 구현 할 수 있다.
여기에 각 bonus damage 옵션을 추가한다면
#include <iostream>
class IBonusDamage {
protected:
public:
IBonusDamage() {}};
virtual ~IBonusDamage() {};
virtual double getRealDamage(double baseDamage) = 0;
};
class CriticalDamage : public IBonusDamage {
public:
CriticalDamage() {}
double getRealDamage(double baseDamage) override {
return baseDamage * 2;
};
};
이런 식으로 IBonusDamage를 상속받는 부가 옵션들은 계속 추가할 수 있다.
그렇다면 이것을 weapon에 추가하기 위해, 각 Weapon Class에서 IBonusDamage pointer를 가지고, weapon에 각각의 option을 추가할 수 있게 하자.
#include <iostream>
class IBonusDamage {
protected:
public:
IBonusDamage() {};
virtual ~IBonusDamage() {};
virtual double getRealDamage(double baseDamage) = 0;
};
class BaseDamage : public IBonusDamage {
public:
BaseDamage() : IBonusDamage() {}
double getRealDamage(double baseDamage) override {
return baseDamage;
};
};
class CriticalDamage : public IBonusDamage {
public:
CriticalDamage() {}
double getRealDamage(double baseDamage) override {
return baseDamage * 2;
};
};
class burnningDamage : public IBonusDamage {
public:
burnningDamage() {}
double getRealDamage(double baseDamage) override {
return baseDamage * 1.1;
};
};
class Weapon {
double BaseDeal;
double getBaseDamage() {
return BaseDeal * 100;
}
public:
std::shared_ptr<IBonusDamage> bonusDamageOption;
Weapon(double BaseDeal) : BaseDeal(BaseDeal) {
bonusDamageOption = std::make_shared<BaseDamage>();
}
virtual ~Weapon() {}
void AddNewOption(std::shared_ptr<IBonusDamage> bd) {
bonusDamageOption = bd;
}
double getAttackDamage() {
double TotalDamage = getBaseDamage();
return bonusDamageOption->getRealDamage(TotalDamage);
}
};
int main(){
Weapon* sword = new Weapon(100);
Weapon* staff = new Weapon(50);
Weapon* broken_sword = new Weapon(10);
sword->AddNewOption(std::make_shared<CriticalDamage>());
std::cout << sword->getAttackDamage() << std::endl;
sword->AddNewOption(std::make_shared<burnningDamage>());
std::cout << staff->getAttackDamage() << std::endl;
std::cout << broken_sword->getAttackDamage() << std::endl;
}
이런 식으로 AddNewOption을 추가하여 각 무기에 새로운 보너스 옵션을 추가할 수 있게 되었다.
다만 이렇게 하나의 옵션만 주는 것이 아니라, 여러 개의 옵션을 주고 싶다면?
그리고 그 옵션들이 계속 추가되거나, 아예 랜덤하게 다른 옵션으로 자주 변경이 된다면?
-> decorator 패턴을 이용할 수 있다.
->
#include <iostream>
class IBonusDamage {
protected:
std::shared_ptr<IBonusDamage> pbonusDam;
public:
IBonusDamage() { pbonusDam = nullptr; };
IBonusDamage(std::shared_ptr<IBonusDamage> pbonusDam) : pbonusDam(pbonusDam) {};
virtual ~IBonusDamage() {};
virtual double getRealDamage(double baseDamage) = 0;
};
class BaseDamage : public IBonusDamage {
public:
BaseDamage() : IBonusDamage() {}
double getRealDamage(double baseDamage) override {
return baseDamage;
};
};
class poisonDamage : public IBonusDamage {
public:
poisonDamage(std::shared_ptr<IBonusDamage> pbonusDam) : IBonusDamage(pbonusDam) {}
double getRealDamage(double baseDamage) override {
return pbonusDam->getRealDamage(baseDamage * 1.05);
};
};
class burnningDamage : public IBonusDamage {
public:
burnningDamage(std::shared_ptr<IBonusDamage> pbonusDam) : IBonusDamage(pbonusDam) {}
double getRealDamage(double baseDamage) override {
return pbonusDam->getRealDamage(baseDamage * 1.1);
};
};
class CriticalDamage : public IBonusDamage {
public:
CriticalDamage(std::shared_ptr<IBonusDamage> pbonusDam) : IBonusDamage(pbonusDam) {}
double getRealDamage(double baseDamage) override {
return pbonusDam->getRealDamage(baseDamage * 2);
};
};
class dodgeDamage : public IBonusDamage {
public:
dodgeDamage(std::shared_ptr<IBonusDamage> pbonusDam) : IBonusDamage(pbonusDam) {}
double getRealDamage(double baseDamage) override {
return pbonusDam->getRealDamage(baseDamage * 0.3);
};
};
class Weapon {
double BaseDeal;
double getBaseDamage() {
return BaseDeal * 100;
}
public:
std::shared_ptr<IBonusDamage> bonusDamageOption;
Weapon(double BaseDeal) : BaseDeal(BaseDeal) {
bonusDamageOption = std::make_shared<BaseDamage>();
}
virtual ~Weapon() {}
void AddNewOption(std::shared_ptr<IBonusDamage> bd) {
bonusDamageOption = bd;
}
double getAttackDamage() {
double TotalDamage = getBaseDamage();
return bonusDamageOption->getRealDamage(TotalDamage);
}
};
int main() {
Weapon* sword = new Weapon(10);
std::cout << sword->getAttackDamage() << std::endl;
sword->AddNewOption(std::make_shared<poisonDamage>(sword->bonusDamageOption));
std::cout << sword->getAttackDamage() << std::endl;
sword->AddNewOption(std::make_shared<CriticalDamage>(sword->bonusDamageOption));
sword->AddNewOption(std::make_shared<burnningDamage>(sword->bonusDamageOption));
std::cout << sword->getAttackDamage() << std::endl;
sword->AddNewOption(std::make_shared<dodgeDamage>(sword->bonusDamageOption));
std::cout << sword->getAttackDamage() << std::endl;
std::cout << sword->getAttackDamage() << std::endl;
}
이런 식으로 하면 제한없이 bonus option을 추가할 수 있고, Weapon class 내부 IBonusOption pointer를 초기화하여 처음부터 옵션을 새로 줄 수도 있다.
'Design Pattern' 카테고리의 다른 글
iterator pattern (0) | 2022.11.20 |
---|---|
Template Method (0) | 2022.09.06 |
Observer Pattern (0) | 2022.08.14 |
factory method (0) | 2022.06.07 |
Composite Pattern (0) | 2022.05.18 |