본문 바로가기
프로그래밍 이야기/C++ 기초

[C++]Design Pattern - Decorator Pattern

by Mulder5 2020. 12. 18.
반응형

[문제점] 미사일을 발사하는 우주선이 있다. 가령 어떤 아이템을 획득하면 왼쪽 미사일이 추가되고, 다시금 아이템을 획득하면 오른쪽 미사일도 추가되는 이러한 기능을 만들어보자.

#include <iostream>

using namespace std;

class SpaceCraft
{
    int color;
    int speed;
public:
    void Fire() { cout << "Space Craft : ----------" << endl; }
};

// 기존 클래스에 새로운 기능을 추가하는 가장 간단한 방법은 상속.
class LeftMissile : public SpaceCraft
{
public:
    void Fire()
    {
        SpaceCraft::Fire(); // 기존 기능 수행
        cout << "Left Missile : >>>>>>>>>" << endl;
    }
};

int main() {

    SpaceCraft sc;
    sc.Fire();
    
    LeftMissile_ lm;
    lm.Fire();

	return 0;
}

기존 클래스에 새로운 기능을 추가하는 가장 간단한 방법은 상속이다. 그러나 위의 경우 SpaceCraft와는 전혀 관계 없는 새로운 우주선(새로운 인스턴스)가 만들어졌다. 기능을 부여한 것이 아니고 기능이 부여된 새로운 객체를 만든 것이다. 

이번에는 이를 개선해보기 위해 구성(Composition)을 통한 기능 추가를 시도해보자. 더 나아가 나머지 RightMissile도 추가해준다.

#include <iostream>

using namespace std;

class SpaceCraft
{
    int color;
    int speed;
public:
    void Fire() { cout << "Space Craft : ----------" << endl; }
};

// 구성(Composition)을 통한 기능의 추가
class LeftMissile
{
    SpaceCraft* craft;
public:
    LeftMissile(SpaceCraft* p):craft(p){}
    void Fire()
    {
        craft->Fire(); // 기존 기능 수행
        cout << "Left Missile : >>>>>>>>>" << endl;
    }
};

class RightMissile
{
    SpaceCraft* craft;
public:
    RightMissile(SpaceCraft* p):craft(p){}
    void Fire()
    {
        craft->Fire(); // 기존 기능 수행
        cout << "Left Missile : >>>>>>>>>" << endl;
    }
};

int main() {

    SpaceCraft sc;
    sc.Fire();

    // 구성(Composition)을 통한 기능의 추가
    // 기존 객체에 새로운 기능이 추가된다.
    LeftMissile lm(&sc);
    lm.Fire();
    
    RightMissile rm(&sc);   // -> 이건 우주선에 Left를 추가하고 Right가 추가된 것.
    lm2.Fire();

    return 0;
}

구성을 통해 두개의 미사일이 추가되었지만 이 방식보다는 다음과 같이 한번에 두 미사일을 추가할 수 있어야 하지 않을까? 

RightMissile rm(&lm2);

그러나 RightMissile은 LeftMissile을 받을 수 없기 때문에 에러이다. 이러한 구현을 위해서는 다시금 개선이 필요하다. 즉 우주선과 Missile들의 공통의 기반 클래스가 필요하겠다. 다음과 같이 Component객체를 우주선과 미사일들이 상속했고

#include <iostream>

using namespace std;

// 우주선과 기능추가클래스(미사일)의 공통의 기반 클래
struct Component
{
    virtual void Fire() = 0;
    virtual ~Component() {}
};



class SpaceCraft : public Component
{
    int color;
    int speed;
public:
    void Fire() { cout << "Space Craft : ----------" << endl; }
};


// 구성(Composition)을 통한 기능의 추가
class LeftMissile : public Component
{
    Component* craft;
public:
    LeftMissile(Component* p):craft(p){}
    void Fire()
    {
        craft->Fire(); // 기존 기능 수행
        cout << "Left Missile : >>>>>>>>>" << endl;
    }
};

class RightMissile
{
    Component* craft;
public:
    RightMissile(Component* p):craft(p){}
    void Fire()
    {
        craft->Fire(); // 기존 기능 수행
        cout << "Right Missile : >>>>>>>>>" << endl;
    }
};

int main() {

    SpaceCraft sc;
    //sc.Fire();


    LeftMissile lm2(&sc);
    //lm2.Fire();

    //RightMissile rm(&sc);   // -> 이건 Left를 추가하고 Right가 추가된 것.
    //lm2.Fire();

    // lm2로 생성되어야 한꺼전에 Left, Right가 설정된 것으로 볼 수 있다.
    // 기능 추가된 객체에 다시 기능 추 -> 그러나 에러(RightMissile은 LeftMissile을 받을 수 없다.
    //  --> 공통의 기반 클래스가 있어야 한다. (우주선과 기능추가 객체는 동일한 기반 클래스를 가져야 한다.)
    RightMissile rm(&lm2);
    rm.Fire();

    return 0;
}

그러나 이 경우에도 불편한 점이 있다. 모든 객체가 Component를 포함해야 하는 것이다. 이를 위해 한번더 개선을 해보자. 우주선과 기능추가클래스(미사일)에 대한 공통의 기반 클래스를 만든다.

#include <iostream>

using namespace std;

// 우주선과 기능추가클래스(미사일)의 공통의 기반 클래
struct Component
{
    virtual void Fire() = 0;
    virtual ~Component() {}
};

// 기능 추가 클래스의 공통 기반클래
class IDecorator : public Component
{
    Component* craft;
public:
    IDecorator( Component* p):craft(p){}
    void Fire(){ craft->Fire();}
};

class SpaceCraft : public Component
{
    int color;
    int speed;
public:
    void Fire() { cout << "Space Craft : ----------" << endl; }
};


// 구성(Composition)을 통한 기능의 추가
class LeftMissile : public IDecorator
{
    Component* craft;
public:
    LeftMissile(Component* p):IDecorator(p){}
    void Fire()
    {
        IDecorator::Fire(); // 기존 기능 수행
        cout << "Left Missile : >>>>>>>>>" << endl;
    }
};

class RightMissile : public IDecorator
{
    Component* craft;
public:
    RightMissile(Component* p):IDecorator(p){}
    void Fire()
    {
        IDecorator::Fire(); // 기존 기능 수행
        cout << "Right Missile : >>>>>>>>>" << endl;
    }
};

int main() {

    SpaceCraft sc;
    //sc.Fire();


    LeftMissile lm2(&sc);
    //lm2.Fire();


    // lm2로 생성되어야 한꺼전에 Left, Right가 설정된 것으로 볼 수 있다.
    // 기능 추가된 객체에 다시 기능 추 -> 그러나 에러(RightMissile은 LeftMissile을 받을 수 없다.
    //  --> 공통의 기반 클래스가 있어야 한다. (우주선과 기능추가 객체는 동일한 기반 클래스를 가져야 한다.)
    RightMissile rm(&lm2);
    rm.Fire();

    return 0;
}

 

Decorator Pattern

원본 클래스 -> Concrete Component (SpaceCraft)
객체(우주선)에 기능을 추가하는 클래스 -> Contrete Decorator
1. 객체에 동적으로 새로운 서비스를 추가할 수 있다.
2. 기능 추가를 위해 서브 클래스를 사용하는 것 보다 융통성 있는 방법을 제공한다.

반응형