문제

"공장"디자인 패턴 구현에 대한 다음 코드가 있습니다.

class Pen{
public:
     virtual void Draw() = 0;
};

class RedPen : public Pen{
public:
     virtual void Draw(){
         cout << "Drawing with red pen" << endl;
     }
};

class BluePen : public Pen{
public:
     virtual void Draw(){
         cout << "Drawing with blue pen" << endl;
     }
};

auto_ptr<Pen> createPen(const std::string color){
     if(color == "red")
         return auto_ptr<Pen>(new RedPen);
     else if(color == "blue")
         return auto_ptr<Pen>(new BluePen);
}

그러나 "C ++ 템플릿"을 사용하여 더 나은 방법으로 수행 할 수 있다고 들었습니다. 누구든지 그것이 어떻게 수행되는지와 템플릿 접근 방식 이이보다 더 나은 방법을 도울 수 있습니까?

이견있는 사람

도움이 되었습니까?

해결책

게시 한 예에서 공장이나 템플릿 접근 방식은 나에게 의미가 없습니다. 내 솔루션에는 펜 클래스의 데이터 멤버가 포함됩니다.

class Pen {
public:
    Pen() : m_color(0,0,0,0) /* the default colour is black */
    {            
    }

    Pen(const Color& c) : m_color(c)
    {
    }

    Pen(const Pen& other) : m_color(other.color())
    {
    }

    virtual void Draw()
    {
        cout << "Drawing with a pen of color " << m_color.hex();
    }
    void setColor(const Color& c) { m_color = c; }
    const Color& color() const { return m_color; }
private:
    Color m_color;
};

class Color {
public:
    Color(int r, int g, int b, int a = 0) :
        m_red(r), m_green(g), m_blue(other.blue()), m_alpha(a)  
    {
    }

    Color(const Color& other) : 
        m_red(other.red()), m_green(other.green()), 
        m_blue(other.blue()), m_alpha(other.alpha())
    {
    }

    int red() const { return m_red; }
    int green() const  { return m_green; }
    int blue() const { return m_blue; }
    int alpha() const { return m_alpha; }

    std::string hex() const
    {
        std::ostringstream os;
        char buf[3];
        os << "#";

        sprintf(buf, "%2X", red());
        os << buf;

        sprintf(buf, "%2X", green());
        os << buf;

        sprintf(buf, "%2X", blue());
        os << buf;

        sprintf(buf, "%2X", alpha());
        os << buf;

        return os.str();
    }

private:
    int m_red;
    int m_green;
    int m_blue;
    int m_alpha;
}

물론, 색상 클래스는 사용하는 드로잉 API에 조정되어야하며 아마도이 제품 (다른 컬러 공간 등)보다 더 발전 할 수 있습니다.

왜 템플릿이 아닌가?

템플릿을 사용하는 것이 합리적이지 않은 이유는 (아마도) 다른 도면 작업의 유일한 차이점은 색상 변수이기 때문입니다. 따라서 템플릿을 사용하거나 다른 클래스를 수동으로 선언하면 유사한 코드를 복제합니다. 이렇게하면 프로그램이 크게 커지고 속도가 느려집니다.

따라서 드로우 함수는 색상을 인수로 취하거나 (내 예에서와 같이) 색상을 클래스 데이터 구성원으로 사용해야합니다.

다른 팁

또 다른 방법은 동적으로 등록하는 것입니다 창조자 역동적 인 공장 객체에 기능합니다.

BluePen *create_BluePen() { return new BluePen; }
static bool BluePen_creator_registered = 
                       Factory::instance()->registerCreator("BluePen", 
                                                            create_BluePen);

이와 같이하는 데있어 흥미로운 효과 중 하나는 정적 부울 변수가 BluePen-creator-registered 사전에 설정됩니다 main() 따라서 등록을 자동화하기 시작합니다.

이 라인은 때때로 평범한 매크로를 통해 만들어집니다.

#define METAIMPL( _name ) \
_name *create_ ## _name() { return new _name; } \
static bool _name ## _creator_registered = \
                        Factory::instance()->registerCreator(# _name, \
                                                             create_ ## _name)

... 생성자에 가깝게 사용됩니다

METAIMPL( BluePen ); // auto registers to the Factory

BluePen::BluePen() : Pen() {
   // something
}

그러면 공장의 임무는 이것들을 저장하고 조회하는 것입니다. 창조자 기능. 나는 나머지를 그대로 둡니다 운동 ;) 즉, metadecl 매크로 사용

더 많은 정보를 원하시면 참조하십시오 여기 장에서 4.1 메타 정보 또한 가능성을 포함하도록 확장하는 방법도 포함 조사관 특징

나는 이것을 사용하여 배웠다 ET ++ 그것은 오래된 MacApp를 C ++ 및 X11로 포트하는 프로젝트였습니다. 그것의 노력으로 Eric Gamma 등은 생각하기 시작했습니다. 디자인 패턴

그리고 ... (2011 년 5 월 7 일) 마침내 Github에 모범을 이루기 위해 돌아 왔습니다.
https://github.com/epatel/cpp-factory

당신의 공장은 괜찮습니다. 나는 그것을 취한다 BluePen 그리고 그저 예제 클래스 이름이었습니다. 다음 조건이 충족되면 템플릿을 사용할 수 있습니다.

Compile Time (즉, 코드를 작성할 때)에 특정 유형을 반환하려는 것을 알면 템플릿을 사용하십시오. 그렇지 않으면, 당신은 할 수 없습니다.

즉, 코드에서 다음을 수행 할 수 있습니다.

template<typename PenType>
auto_ptr<Pen> createPen(){
    return auto_ptr<Pen>(new PenType);
}

제자리에 있으면 그런 다음 사용할 수 있습니다.

...
auto_ptr<Pen> p = createPen<BluePen>();
...

그러나 그 템플릿 우리의 BluePen, 런타임에 유형으로 설정된 변수가 될 수 없습니다. 예에서는 문자열을 전달하는데, 물론 런타임에 설정할 수 있습니다. 따라서 C ++ 템플릿을 사용할 수 있다는 것을 읽으면 권장 사항은 조건부로만 사실입니다. 그러면 펜이 생성 할 결정이 컴파일 시간에 이미 수행됩니다. 해당 조건이 맞으면 템플릿 솔루션은 그만큼 옳은 일. 런타임에 비용이 들지 않으며 정확히 필요한 것이 될 것입니다.

색상을 위해 특별한 빈 클래스를 선언하면 템플릿을 사용하여 모든 것을 수행 할 수 있습니다. 이를 위해서는 컴파일 타임에 모든 색상 선택을 제공해야합니다. 이렇게하면 가상 메소드가있는 기본 클래스를 사용하지 않아도됩니다.

struct Red{};
struct Blue{};

template < typename Color >
class Pen{};

template <>
class Pen< Red >
{
     void Draw(){
         cout << "Drawing with red pen" << endl;
     }
};

template <>
class Pen< Blue >
{
     void Draw(){
         cout << "Drawing with blue pen" << endl;
     }
};

template < typename Color >
std::auto_ptr< Pen< Color > > createPen()
{
     return auto_ptr< Pen< Color > >(new Pen< Color >());
}

일반 객체 공장 클래스를 템플릿 클래스로 쓸 수도 있습니다 (또는 이것에 잘 설명 된 것을 사용하십시오. gamedev.net 기사).

이런 식으로, 코드에 공장이 둘 이상인 경우 각 공장을 정의하는 작업이 적습니다.

다른 답변에 대한 보충으로, 공장 패턴 대 템플릿 사용에 대해 논의하기 위해 :

템플릿을 사용하는 주요 (그리고 가장 간단한) 이유는 작동하는 데이터 유형을 제외하고 모든 경우에 코드가 동일하기 때문입니다. 여기에는 STL 컨테이너가 있습니다. 공장 기능 Createverctor ( "String")를 작성하고 각 컨테이너를 수동으로 입력 할 수는 있지만 분명히 차선책입니다.

코드가 다른 경우에도 데이터 유형뿐만 아니라 템플릿 전문화를 사용할 수 있지만 많은 경우 공장 기능이 더 합리적입니다.

예를 들어, 데이터베이스 추상화 라이브러리를 고려하십시오. 라이브러리가 "DB :: 드라이버"처럼 사용할 수 있도록 템플릿 전문화를 사용할 수 있습니다. 그러나 코드의 모든 곳에서 데이터베이스 유형을 입력하거나 (처음에 라이브러리를 쓸모 없게 만들거나) 인터페이스 유형 DB :: 드라이버 클래스에 대한 케이스를 수행해야합니다.

이 예에서는 db :: get_driver (ODBC)라고 말하고 인터페이스 유형으로 적절한 클래스 캐스트를 다시받는 것이 더 직관적입니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top