代码之家  ›  专栏  ›  技术社区  ›  Neil Williams

有可能使C++中的工厂符合开放/封闭的原则吗?

c++
  •  1
  • Neil Williams  · 技术社区  · 17 年前

    在我正在C++中工作的项目中,我需要为消息在消息中出现时创建对象。我目前正在使用 factory method pattern 要隐藏对象的创建,请执行以下操作:

    // very psuedo-codey
    Message* MessageFactory::CreateMessage(InputStream& stream)
    {
        char header = stream.ReadByte();
    
        switch (header) {
        case MessageOne::Header:
            return new MessageOne(stream);
        case MessageTwo::Header:
            return new MessageTwo(stream);
        // etc.
        }
    }
    

    在C i中,我会先考虑一下工厂的首次使用(奖金问题:这是一个反射的好用,对吧?),但是因为C++缺乏反射,这是不可能的。我曾考虑过使用某种注册表,以便消息在启动时向工厂注册,但这受到 non-deterministic (or at least implementation-specific) static initialization order problem

    因此,问题是,是否有可能在C++中实现这种类型的工厂,同时尊重开放/封闭的原则,以及如何?

    编辑:显然我想得太多了。我想问一个“在C++中如何实现这一点”的问题,因为用其他语言进行反射非常容易。

    5 回复  |  直到 17 年前
        1
  •  2
  •   Foredecker    17 年前

    我认为开放式/封闭式方法和干式方法是好的原则。但它们并不神圣。目标应该是使代码可靠和可维护。如果您必须执行非自然的行为来遵守O/C或DRY,那么您可能只是让代码变得不必要的更复杂,而没有任何实质性的好处。

    这是 something I wrote 几年前关于我是如何做出判断的。

        2
  •  1
  •   dirkgently    17 年前

    您不需要让代码同时遵循所有可能的原则。我们的目标应该是尽可能多地坚持这些范式,而不是更多。不要过度设计您的解决方案——否则,您很可能会得到意大利面条式的代码。

        3
  •  1
  •   Community Mohan Dere    9 年前

    我在另一个关于C++工厂的问题上回答了问题。请看 there 如果一个灵活的工厂感兴趣。我试图描述一种从ET++到使用宏的老方法,这种方法对我来说非常有效。

    该方法基于宏,易于扩展。

    ET++ 是一个将旧Mac App移植到C++和X11的项目。在它的努力下,Eric Gamma等开始思考 设计模式

        4
  •  1
  •   stefanB    17 年前

    您可以将创建消息的类(MessageOne、MessageTwo…)转换为消息工厂,并在初始化时向顶级MessageFactory注册它们。

    消息工厂可以保存MessageX::Header的映射->MessageXFactory类映射的实例。

    在CreateMessage中,您将根据消息头找到MessageXFactory的实例,检索对MessageXFactory的引用,然后调用将返回实际MessageX实例的方法。

    例子:

    #include <iostream>
    #include <map>
    #include <string>
    
    using namespace std;
    
    struct Message
    {
        static const int id = 99;
        virtual ~Message() {}
        virtual int msgId() { return id; }
    };
    
    struct NullMessage : public Message
    {
        static const int id = 0;
        virtual int msgId() { return id; }
    };
    
    struct MessageOne : public Message
    {
        static const int id = 1;
        virtual int msgId() { return id; }
    };
    
    struct MessageTwo : public Message
    {
        static const int id = 2;
        virtual int msgId() { return id; }
    };
    
    struct MessageThree : public Message
    {
        static const int id = 3;
        virtual int msgId() { return id; }
    };
    
    struct IMessageFactory
    {
        virtual ~IMessageFactory() {}
        virtual Message * createMessage() = 0;
    };
    
    struct MessageOneFactory : public IMessageFactory
    {
        MessageOne * createMessage()
        {
            return new MessageOne();
        }
    };
    
    struct MessageTwoFactory : public IMessageFactory
    {
        MessageTwo * createMessage()
        {
            return new MessageTwo();
        }
    };
    
    struct TopMessageFactory
    {
        Message * createMessage(const string& data)
        {
            map<string, IMessageFactory*>::iterator it = msgFactories.find(data);
            if (it == msgFactories.end()) return new NullMessage();
    
            return (*it).second->createMessage();
        }
    
        bool registerFactory(const string& msgId, IMessageFactory * factory)
        {
            if (!factory) return false;
            msgFactories[msgId] = factory;
            return true;
        }
    
        map<string, IMessageFactory*> msgFactories;
    };
    
    int main()
    {
        TopMessageFactory factory;
        MessageOneFactory * mof = new MessageOneFactory();
        MessageTwoFactory * mtf = new MessageTwoFactory();
    
        factory.registerFactory("one", mof);
        factory.registerFactory("two", mtf);
    
        Message * msg = factory.createMessage("two");
        cout << msg->msgId() << endl;
    
        msg = factory.createMessage("one");
        cout << msg->msgId() << endl;
    }
    
        5
  •  0
  •   ddevienne    17 年前

    除了开玩笑,这是一种情况,我会使用一个小的模板化工厂类(如果您将char消息类型放在非类模板arg中,或者仅将该char作为状态,则该类是无状态的)来接受流&新的模板是否在其T模板arg上传递流&然后把它还给我。您将需要一个小的注册器类来声明为具有全局作用域的静态类,并向管理器注册具体的T实例化工厂(通过抽象基类指针)(我们有一个接受“工厂域”键的通用类)。在您的情况下,我不会使用映射,而是直接使用256“插槽”数组来放置factory_base*。