3

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

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

有什么想法吗

4

6 回答 6

13

另一种方法是将创建者函数动态注册到动态工厂对象。

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
}

然后工厂的任务将是存储和查找这些创建者函数。我把剩下的留作练习;)即使用 METADECL 宏

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

我从使用ET++中学到了这一点,这是一个将旧 MacApp 移植到 C++ 和 X11 的项目。在此过程中,Eric Gamma 等人开始考虑设计模式

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

于 2009-02-10T21:57:08.910 回答
5

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

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

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

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

有了它,你就可以像这样使用它了

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

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

于 2009-01-25T23:03:25.353 回答
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 进行调整——并且可能比这个更高级(不同的颜色空间等)。

为什么不是模板?

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

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

于 2009-01-04T11:34:37.730 回答
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 >());
}
于 2009-01-30T11:21:34.060 回答
1

您可以将通用对象工厂类编写为模板类(或使用本gamedev.net 文章中详细描述的类)。

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

于 2012-02-01T05:43:06.260 回答
0

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

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

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

例如,考虑一个数据库抽象库。可以使用模板特化,这样库就可以像“db::driver”一样使用。但这会迫使您在代码中的任何地方输入数据库类型(首先使库有点无用......),或者对接口类型 db::driver 类执行案例。

在这个例子中,更直观的是说 db::get_driver(odbc) 并将正确的类转换为接口类型。

于 2009-01-04T11:43:27.603 回答