【问题标题】:Factory method implementation - C++工厂方法实现 - C++
【发布时间】:2009-01-04 10:55:44
【问题描述】:

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

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

任何想法

【问题讨论】:

    标签: c++ templates design-patterns factory


    【解决方案1】:

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

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

    这样做的一个有趣效果是静态布尔变量BluePen-creator-registered 将在main() 开始之前设置,从而使注册自动化。

    这些行有时是通过普通的宏做的,即as

    #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
    }
    

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

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

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

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

    【讨论】:

      【解决方案2】:

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

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

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

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

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

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

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

      【讨论】:

        【解决方案3】:

        在您发布的示例中,工厂或模板方法对我来说都没有意义。 我的解决方案涉及 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 函数应该将颜色作为参数,或者(如在我的示例中)将颜色作为类数据成员。

        【讨论】:

        • 这里代码中的引用复制构造函数有什么意义?
        • 这被称为复制构造函数,并且会在令人惊讶的时候被调用。代码写好了,没有重点,只是说我抄笔的时候,就是抄笔的颜色。如果Color 更复杂,这可能会更重要。
        【解决方案4】:

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

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

        【讨论】:

          【解决方案5】:

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

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

          【讨论】:

            【解决方案6】:

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

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

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

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

            在本例中,使用 db::get_driver(odbc) 并将正确的类转换为接口类型更直观。

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多