이번 시간에는 객체 생성 관련 패턴을 알아보겠습니다.

객체 생성 방법

1. 사용자가 직접 객체를 생성(stack or heap)

  • 가장 자유로운 방법이나 객체 생성에 대한 제약이 없음
class Shape
{
public:
	virtual ~Shape() {}
};

class Rect : public Shape
{
public:
};
class Circle : public Shape
{
public:
};

int main()
{
	Rect r;

	Shape* p = new Rect;

}

2. static 멤버 함수를 사용한 객체 생성

  • 생성자를 private 처리하고 객체 생성 함수를 제공
  • 객체 생성을 한 곳에서 수행
  • 다양한 제약 조건을 만들 수 있음(개수 제한, 자원 공유 등)
  • 객체 생성 함수를 함수의 생성 인자로 전달 할 수 있음
class Shape
{
public:
	virtual ~Shape() {}
};

class Rect : public Shape
{
    Rect() {}  // private 생성자.
public:
    static Shape* Create() { return new Rect;}
};
class Circle : public Shape
{
    Circle() {}
public:
    static Shape* Create() { return new Circle;}
};

// 도형을 만들어서 그림을 그리는 함수, 함수 포인터 전달
void CreateAndDraw(Shape* (*f)() )
{
    Shape* p = f();
    p->Draw();
}


int main()
{
    CreateAndDraw(&Rect::Create );
}

3. 객체 생성을 위한 전용 클래스 사용

  • 객체 생성을 한곳에 집중시킴
  • 객체 본래의 기능과, 생성을 위한 코드를 분리
class Shape
{
public:
	virtual ~Shape() {}
};

class Rect : public Shape
{
    Rect() {}
public:
	friend class ShapeFactory;
};

class Circle : public Shape
{
    Circle() {}
public:
	friend class ShapeFactory;
};

class ShapeFactory
{
public:
	Shape* CreateShape( int type )
	{
		Shape* p = 0;
		switch( type )
		{
		case 1: p = new Rect; break;
		case 2: p = new Circle; break;
		}
		return p;
	}
};

int main()
{
	ShapeFactory factory;
	Shape* p = factory.CreateShape(1);

}

4. 기존 존재하는 객체를 복사해서 새로운 객체 생성

  • prototype
class Shape
{
public:
	virtual ~Shape() {}

    virtual Shape* Clone() = 0;
};

class Rect : public Shape
{
public:
    virtual Shape* Clone() { return new Rect(*this);}
};
class Circle : public Shape
{
public:
    virtual Shape* Clone() { return new Circle(*this);}
};

int main()
{
	Shape* p = new Rect;

    Shape* p2 = p->Clone();
}

Singleton pattern

정의

  • 클래스의 인스턴스가 오직 하나임을 보장하며 이에 대한 접근은 어디에서 든지 하나로 통일하여 제공

구현

  • 생성자를 private 처리
  • static 멤버 함수를 통해서 오직 하나의 객체를 생성한 후 참조를 반환
#include <iostream>
using namespace std;

// CRTP : Curiously Recurring Template Pattern
template<typename TYPE> class Singleton
{
protected:
    Singleton() { }
    Singleton(const Singleton&) = delete;
    void operator=(const Singleton&) = delete;
public:
    static TYPE& getInstance()
    {
        static TYPE instance;
        return instance;
    }
};

class Mouse : public Singleton< Mouse >
{
};

int main()
{
    Mouse& c1 = Mouse::getInstance();
}

Factory pattern

정의

  • 새로운 객체가 필요 할 때 다형적 생성이 가능한 구조를 제공하는 패턴
#include <iostream>
#include <vector>
#include <map>
#include "Singleton.hpp"
using namespace std;

class Shape
{
public:
    virtual void Draw() { cout << "Draw Shape" << endl;}
};

class ShapeFactory
{
    MAKE_SINGLETON(ShapeFactory)
    typedef Shape* (*CREATOR)();
    map<int, CREATOR> create_map;
public:
    void Register( int type, CREATOR f )
    {
        create_map[type] = f;
    }

    Shape* CreateShape(int type )
    {
        Shape* p = 0;
        auto ret = create_map.find( type );
        if ( ret == create_map.end() )
            return 0;
        p = create_map[type]();

        return p;
    }
};

struct RegisterShape
{
    RegisterShape( int type, Shape*(*f)() )
    {
        ShapeFactory& factory = ShapeFactory::getInstance();

        factory.Register(type, f);
    }
};

// 모든 도형이 지켜야 하는 규칙을 매크로로 제공
#define DECLARE_SHAPE( classname )                  \
    static Shape* Create() { return new classname;}      \
    static RegisterShape rs;

#define IMPLEMENT_SHAPE( type, classname )                \
    RegisterShape classname::rs(type, &classname::Create);

class Circle : public Shape
{
public:
    virtual void Draw() { cout << "Circle Rect" << endl;}

    DECLARE_SHAPE( Circle )
};
IMPLEMENT_SHAPE( 1, Circle )


class Triangle : public Shape
{
public:
    virtual void Draw() { cout << "Triangle Rect" << endl;}

    DECLARE_SHAPE( Triangle )
};
IMPLEMENT_SHAPE( 2, Triangle )

int main()
{
    ShapeFactory& factory = ShapeFactory::getInstance();

    vector<Shape*> v;

    while( 1 )
    {
        int cmd;
        cin >> cmd;

        //
        if ( cmd >=1 && cmd <= 5 )
        {
            Shape* p = factory.CreateShape(cmd);

            if ( p != 0 )
                v.push_back( p );
        }
        else if ( cmd == 9 )
        {
            for ( auto p : v )
                p->Draw(); // 다형성
        }
    }
}

factory 패턴 응용 -> Prototype 패턴

  • 같은 타입에, 데이터가 다를 경우 -> 이것을 복사해서 전달하는 형태
#include <iostream>
#include <vector>
#include <map>
#include "Singleton.hpp"
using namespace std;

class Shape
{
public:
    virtual void Draw() { cout << "Draw Shape" << endl;}

    virtual Shape* Clone() = 0;
};

class Rect : public Shape
{
public:
    virtual void Draw() { cout << "Draw Rect" << endl;}

    static Shape* Create() { return new Rect;}

    virtual Shape* Clone() { return new Rect(*this);}
};

class Circle : public Shape
{
public:
    virtual void Draw() { cout << "Circle Rect" << endl;}

    static Shape* Create() { return new Circle;}

    virtual Shape* Clone() { return new Circle(*this);}
};

class ShapeFactory
{
    MAKE_SINGLETON(ShapeFactory)


    map<int, Shape*> protype_map;

public:
    void Register( int type, Shape* sample )
    {
        protype_map[type] = sample;
    }

    Shape* CreateShape(int type )
    {
        Shape* p = 0;
        auto ret = protype_map.find( type );
        if ( ret == protype_map.end() )
            return 0;

        p = protype_map[type]->Clone();

        return p;
    }
};

int main()
{
    ShapeFactory& factory = ShapeFactory::getInstance();

    Rect* r1 = new Rect;// 빨간색 크기 5
    Rect* r2 = new Rect;// 파란색 크기 10

    // 공장에 객체 등록
    factory.Register( 1, r1);
    factory.Register( 2, r2);

    vector<Shape*> v;

    while( 1 )
    {
        int cmd;
        cin >> cmd;

        if ( cmd >=1 && cmd <= 5 )
        {
            Shape* p = factory.CreateShape(cmd);

            if ( p != 0 )
                v.push_back( p );
        }
        else if ( cmd == 9 )
        {
            for ( auto p : v )
                p->Draw(); // 다형성
        }
    }
}

Abstract Factory pattern

정의

  • 여러 객체의 군을 생성하기 위한 인터페이스를 제공
#include <iostream>
#include <string.h>
using namespace std;

struct IEdit
{
    virtual void Draw() = 0;
    virtual ~IEdit() {}
};

struct IButton
{
    virtual void Draw() = 0;
    virtual ~IButton() {}
};

struct WinButton : public IButton { void Draw() { cout << "Draw WinButton" << endl;}};
struct GTKButton : public IButton{ void Draw() { cout << "Draw GTKButton" << endl;}};

struct WinEdit : public IEdit { void Draw() { cout << "Draw WinEdit" << endl;}};
struct GTKEdit : public IEdit { void Draw() { cout << "Draw GTKEdit" << endl;}};

//-------------------------------------
// Factory 의 공통의 기반 클래스
struct IFactory
{
    virtual IButton* CreateButton() = 0;
    virtual IEdit*   CreateEdit() = 0;
    virtual ~IFactory() {}
};

struct WinFactory : public IFactory
{
    IButton* CreateButton() { return new WinButton;}
    IEdit*   CreateEdit()   { return new WinEdit;}
};

struct GTKFactory : public IFactory
{
    IButton* CreateButton() { return new GTKButton;}
    IEdit*   CreateEdit()   { return new GTKEdit;}
};

int main(int argv, char** argc)
{
    IFactory* pFactory;
    if ( strcmp(argc[1], "Windows") == 0)
        pFactory = new WinFactory;
    else
        pFactory = new GTKFactory;

    IButton* pBtn = pFactory->CreateButton();
    pBtn->Draw();
}

Factory Method pattern

정의

  • 객체를 생성하기 위한 인터페이스를 정의 하지만, 어떤 인스턴스로 생석 할 지에 대한 결정은 하위 클래스에서 재정의
  • Factory Method 패턴에서는 클래스의 인스턴스 생성 시점을 하위 클래스로 연기함
  • template method와 유사한 형태
    • 리마인드 : 템플릿 메소드는 알고리즘이나 정책을 하위 클래스에서 재정의 하는 것
#include <iostream>
using namespace std;

struct IEdit
{
    virtual void Draw() = 0;
    virtual ~IEdit() {}
};
struct IButton
{
    virtual void Draw() = 0;
    virtual ~IButton() {}
};

struct WinButton : public IButton { void Draw() { cout << "Draw WinButton" << endl;}};
struct GTKButton : public IButton { void Draw() { cout << "Draw GTKButton" << endl;}};

struct WinEdit : public IEdit { void Draw() { cout << "Draw WinEdit" << endl;}};
struct GTKEdit : public IEdit { void Draw() { cout << "Draw GTKEdit" << endl;}};

//---------------------------------------------------

class BaseDialog
{
public:
    void Init()
    {
        IButton* pBtn = CreateButton();
        IEdit*   pEdit = CreateEdit();

        pBtn->Draw();
        pEdit->Draw();
    }
    virtual IButton* CreateButton() = 0;
    virtual IEdit*   CreateEdit() = 0;
};


class WinDialog : public BaseDialog
{
public:
    virtual IButton* CreateButton() { return new WinButton;}
    virtual IEdit* CreateEdit()    { return new WinEdit;}
};

class GTKDialog : public BaseDialog
{
public:
    virtual IButton* CreateButton() { return new GTKButton;}
    virtual IEdit* CreateEdit()   { return new GTKEdit;}
};

int main()
{
    WinDialog dlg;
    dlg.Init();
}

Builder pattern

정의

  • 객체를 생성하는 방법과 표현하는 방법을 정의하는 클래스를 별도로 분리하여 서로 다른 표현이라도 이를 생성 할 수 있는 동일한 인터페이스를 제공하도록 함
  • state / strategy 패턴과 유사
    • 전략 패턴은 교체 되는 부분이 -> 알고리즘
    • 상태 패턴은 교체 되는 부분이 -> 행동
#include <iostream>
#include <string>
using namespace std;

// 입학 지원서
using Application = string; // class Application {}

// 지원서의 각 단계의 표현을 만드는 빌더 인터페이스
struct IBuilder
{
    virtual ~IBuilder() {}
    virtual void makeName(string name) =  0;
    virtual void makePhone(string phone) =  0;
    virtual void makeAddress(string addr) =  0;

    virtual Application getResult() = 0;
};

// 지원서 만드는 클래스
class Director
{
    string name = "HONG";
    string phone = "010-111-1111";
    string address = "SEOUL KANGNAMGU";
    IBuilder* pBuilder;
public:
    void setBuilder( IBuilder* p ) { pBuilder = p;}

    Application construct()
    {
        pBuilder->makeName(name);
        pBuilder->makePhone(phone);
    //    pBuilder->makeAddress(address);

        return pBuilder->getResult();
    }
};

class XMLBuilder : public IBuilder
{
    Application app;
public:
    virtual void makeName(string name)
    {
        app += "<NAME>" + name + "</NAME>\n";
    }
    virtual void makePhone(string phone)
    {
        app += "<PHONE>" + phone + "</PHONE>\n";
    }
    virtual void makeAddress(string addr)
    {
        app += "<ADDRESS>" + addr + "</ADDRESS>\n";
    }

    virtual Application getResult() { return app;}
};

class TextBuilder : public IBuilder
{
    Application app;
public:
    virtual void makeName(string name)
    {
        app += name + "\n";
    }
    virtual void makePhone(string phone)
    {
        app += phone + "\n";
    }
    virtual void makeAddress(string addr)
    {
        app +=  addr + "\n";
    }

    virtual Application getResult() { return app;}
};

int main()
{
    Director d;
    XMLBuilder xb;
    d.setBuilder(&xb);

    Application app = d.construct();
    cout << app << endl;

    TextBuilder tb;
    d.setBuilder(&tb);
    app = d.construct();
    cout << app << endl;
}

전체 요약

GoF’s 디자인 패턴의 분류

  • 생성 패턴 - 5종
    • Singleton : 클래스의 인스턴스는 오직 하나임을 보장하며 이에 대한 접근은 어디에서든 하나로만 통일하여 제공
    • Abstract Factory : 상세화된 서브 클래스를 정의 하지 않고도 서로 관련성 있거나 독립적인 여러 객체의 군을 생성하기 위한 인터페이스를 제공
    • Prototype : 견본 인스턴스를 사용하여 생성할 객체의 종류를 명시하고 견본을 복사하여 새로운 객체를 생성
    • Builder : 복잡한 객체를 생성하는 방법과 표현하는 방법을 정의하는 클래스를 별도로 분리하여 서로 다른 표현이라도 이를 생성 할 수 있는 동일한 구축 공정을 제공
    • Factory Method : 객체를 생성하기 위한 인터페이스를 정의 하지만, 어떤 클래스의 인터페이스를 생성 할 지에 대한 결정은 서브 클래스에 위임
  • 구조 패턴 - 7종
    • Adapter, Proxy, Bridge, Facade : 문제를 해결하기 위해 간접층을 만드는 패턴
    • Composite, Decorator : 재귀적 포함을 사용하는 패턴
    • Flyweight
  • 행위 패턴 - 11종
    • template method
    • strategy
    • state
    • iterator
    • visitor
    • observer
    • command
    • memento
    • interpret
    • mediator
    • chain of responsibility