工厂方法实现 - C ++

时间:2009-01-04 10:55:44

标签: c++ templates design-patterns factory

我有“工厂”设计模式实现的以下代码。

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 ++模板”以更好的方式完成。任何人都可以帮助完成它以及模板方法如何比这更好吗?

任何想法

6 个答案:

答案 0 :(得分:12)

另一种方法是将 creator 函数动态注册到动态Factory对象。

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

这样做的一个有趣的影响是静态bool变量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
}

然后Factory的任务是存储和查找这些 creator 函数。我把剩下的作为练习;)即使用METADECL宏

如果您想了解更多信息,请参阅 4.1元信息章节下的here,其中还包括一种扩展方法,包括检查员功能的可能性

我通过使用ET++来学习这一点,这是一个将旧MacApp移植到C ++和X11的项目。为此,Eric Gamma等开始考虑设计模式

并且......(2011年5月7日)最后来到github举一个例子https://github.com/epatel/cpp-factory

答案 1 :(得分:5)

你的工厂很好。我认为BluePen等等只是示例类名。如果满足以下条件,您可以使用模板:

  

当您在编译时(即编写代码时)知道要返回特定类型时,请使用模板。否则,你不能。

这意味着在代码中,你可以这样做:

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

完成后,您可以使用

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

但是,模板参数BluePen不能是在运行时设置为类型的变量。在您的示例中,您传递一个字符串,当然可以在运行时设置。因此,当您读到可以使用C ++模板时,那么该建议只是条件正确 - 那么,当您决定创建哪支笔时,已经在编译时完成了。如果该条件适合,则模板解决方案 正确的事情。它不会在运行时花费任何成本,而且正是您所需要的。

答案 2 :(得分:4)

在您发布的示例中,工厂或模板方法对我来说都不合适。 我的解决方案涉及Pen类中的数据成员。

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进行调整 - 也许比这个更先进(不同的颜色空间等)。

为什么不是模板?

使用模板没有意义的原因是(推测)不同绘图操作之间的唯一区别是颜色变量。因此,通过使用模板(或手动声明不同的类,就像您所做的那样),您将复制类似的代码。这将使你的程序变大,并减慢速度。

因此,绘制函数应该将颜色作为参数,或者(如我的例子中)将颜色作为类数据成员。

答案 3 :(得分:3)

通过声明颜色的特殊空类,您可以使用模板完成所有操作。这要求在编译时提供每种颜色选择。通过这样做,您可以避免使用带有虚方法的基类。

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 >());
}

答案 4 :(得分:1)

您可以将通用对象工厂类编写为模板类(或使用此gamedev.net article中很好描述的那个)。

这样,如果代码中有多个工厂,则定义每个工厂的工作量就会减少。

答案 5 :(得分:0)

作为我的其他答案的补充,只是讨论工厂模式与模板使用:

使用模板的主要(也是最简单)原因是您的代码在每种情况下都是相同的,除了它所处理的数据类型。这里的例子是STL容器。 可以编写工厂函数createVector(“string”),并手动输出每个容器 - 但这显然是次优的。

即使代码不同,不仅是数据类型,也可以使用模板特化 - 但在很多情况下,工厂函数会更有意义。

作为示例,请考虑数据库抽象库。可以使用模板特化,以便可以像“db :: driver”一样使用库。但是这会强制你在代码中的任何地方输入数据库类型(首先使库变得无用......),或者对接口类型db :: driver class执行大小写。

在这个例子中,更直观地说db :: get_driver(odbc)并返回适当的类转换为接口类型。