Design Pattern

Decorator pattern

sseram 2022. 7. 26. 22:32
반응형

 

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 패턴을 이용할 수 있다.

 

    decorator 패턴은

    => A 와 B를 묶어야 한다면 공통의 기반 클래스를
    => A 는 B를 포함할수 있지만 A 자신도 포함한다.
    재귀적 포함을 사용한 "객체의 기능추가" 가 핵싱이다

    composite pattern과 비슷하게 나 자신을 포함하지만, 나 자신을 포함하는 것만이 아닌 그 포함한 것에 기능을 추가해준다는 것이 핵심이다.

 

 

 

 

->

 

#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