【问题标题】:Object instantiation through factory method not giving desire result通过工厂方法实例化对象没有给出期望的结果
【发布时间】:2018-06-06 05:29:39
【问题描述】:

在下面的代码 sn-p 中,我确实需要通过工厂方法实例化对象以调用选定的适配器(即adapterTwovalue),但在通过工厂方法调用时,我无法获得所需的结果。当我们分配静态声明对象的地址(即adapter = &at)时,它可以工作,但使用工厂我通常会得到空白输出。 我也尝试使用 (adapter = new adapterTwo()) 实例化对象,但输出字符串给出空白结果。根据我的要求,我需要填充 connect 函数中的所有 getter,这是一个纯虚函数来构建响应。任何人都可以建议如何使用工厂方法来实现这一点。

    #include <iostream>

    using namespace std;

    class IAdapter
    {
    public:
        enum FactoryList { AdapterOnevalue = 0, AdapterTwovalue };
        virtual void connect() = 0;
        static IAdapter* CreateList(FactoryList);
        virtual ~IAdapter() {}
    };

    class LibraryOne
    {
        string property;
    public:

        void SetConnection(string property)
        {
            this->property = property;
        }

        string getConnection()const
        {
            return property;
        }

    };

    //LibraryTwo
    class LibraryTwo
    {
        string broker;
    public:
        void SetBroker(string broker1)
        {
            this->broker = broker1;

        }

        string getBroker() const
        {       
            return broker;
        }


    };
    //adapterOne
    class AdapterOne : public IAdapter
    {
        LibraryOne one;
        string constring;
    public:

        void SetClientconnection(string constring)
        {
            one.SetConnection(constring);

        }

        string GetClientconnection()
        {

            return one.getConnection();

        }


        void connect()
        {

            constring = GetClientconnection();

        }
    };


    //Adapter to use library two
    class AdapterTwo : public IAdapter
    {
        LibraryTwo two;
        string brokerstring;
    public:

        void SetClientbroker(string constring)
        {
            two.SetBroker(constring);

        }

        string GetClientbroker()
        {

            return two.getBroker();

        }

        void connect()
        {

            string constring = GetClientbroker();
            cout << "final value=" << constring;

        }
    };

    IAdapter* IAdapter::CreateList(FactoryList SelectList)
    {
        IAdapter *ListObject;

        switch (SelectList)
        {
        case AdapterOnevalue:

            ListObject = new  AdapterOne();
            break;
        case AdapterTwovalue:
            ListObject = new AdapterTwo();

            break;
        default:
            ListObject = NULL;

        }

        return ListObject;

    }

    int main()
    {
        IAdapter *adapter = 0;
        //LibraryTwo obj;
        AdapterTwo at;
        at.SetClientbroker("amqp");
        //cout << at.GetClientbroker();
        //adapter = &at;   it works 
        adapter = IAdapter::CreateList(IAdapter::AdapterTwovalue);//it doesn't work
        //Just do the operation now
        adapter->connect();

        return 0;
    }

【问题讨论】:

  • “它不起作用”是什么意思? IAdapter::CreateList 方法是否返回空指针?
  • 你希望适配器是单例的吗?
  • 不,它返回正确的指针,但正如我在我的问题中提到的,字符串输出只是空白。它应该打印 amqp 。
  • @user2907032,您希望看到什么输出?
  • 不相关,但您应该更喜欢 C++ keywords 而不是旧的(现已过时的)macros,因此更喜欢 nullptr 而不是 NULL.. .

标签: c++ design-patterns factory


【解决方案1】:

您可以在下面的分享链接中看到完整的解决方案。

http://coliru.stacked-crooked.com/a/d8b9d32a1fa989c9

这里是解释。

(1) setClientBroker() 或所有其他与适配器相关的设置器功能需要在接口中实现为虚拟函数,默认参数值为“”(空白字符串)。

(2) 您需要始终在派生类中为 setter 使用 override 关键字 (c++11) 功能,以便编译器在编译期间交叉检查是否覆盖了正确的虚方法。

(3) 不要使用本地原始指针,而是始终使用智能指针。下面是 相同的实现链接。

http://coliru.stacked-crooked.com/a/2feea991ee90d4a2

【讨论】:

    【解决方案2】:

    使用您的代码,我期望输出:final value=

    它不会打印final value=amqp,因为您需要在正确的适配器对象上调用SetClientbroker("amqp")(在您的示例中为adapter)。

    无论如何,我会考虑在基类中放置一个虚拟方法SetString,这样你就可以这样做:

    int main()
    {
        IAdapter *adapter = 0;
        //LibraryTwo obj;
        //AdapterTwo at;
        //at.SetClientbroker("amqp");
        //cout << at.GetClientbroker();
        //adapter = &at;   it works 
        adapter = IAdapter::CreateList(IAdapter::AdapterTwovalue);//it doesn't work
        //Just do the operation now
    
        adapter->SetString("amqp");//<---------
    
        adapter->connect();
    
        return 0;
    }
    

    评论后编辑:

    此时您需要投射对象(正如@Aconcagua 所建议的那样)。 但恕我直言,它一点也不优雅。我认为您将失去使用工厂方法获得的好处。

    【讨论】:

    • 不允许修改基类,因为它只能具有纯虚函数的工厂方法,因为它在那里被用作抽象类,因此它不能有任何支持方法。除此之外,只能通过实际类设置属性。所以这里有两个设计原则。 (1)软件实体(类、模块、函数等)应该对扩展开放,对修改关闭”(2)一个类必须只有一个责任。
    【解决方案3】:
    IAdapter* adapter = nullptr;
    AdapterTwo at;
    adapter = IAdapter::CreateList(IAdapter::AdapterTwovalue);
    

    您在此处创建了两个独立对象(如在createList 中调用new):at 和一个adapter 指向。

    AdapterTwo at;
    at.SetClientbroker("amqp");
    

    现在,如果您让adapter 指向at,请确保您得到了预期的输出,但是 other 对象如何知道您在第一个设置中设置的字符串?

    adapter = IAdapter::CreateList(IAdapter::AdapterTwovalue);
    adapter->SetClientbroker("amqp"); // (*) !!!
    

    您也需要在另一个对象上设置代理。作为不同的对象,您甚至可以独立设置代理:

    AdapterTwo at;
    at.SetClientbroker("amqp");
    IAdapter* adapter = IAdapter::CreateList(IAdapter::AdapterTwovalue);
    adapter->SetClientbroker("aconcagua"); // (*) !!!
    

    现在的输出将是(如果您在两个对象上都调用了connect):

    final value=amqp
    final value=aconcagua
    

    仅:标记的行 ((*))不会编译,因为您的基类未提供适当的设置器!

    对于这个问题现在有不同的解决方案。例如,您可以只投射对象:

    // if you are REALLY 100% sure the object is of appropriate type:
    static_cast<AdapterTwo*>(adapter)->setClientBroker("...");
    
    // if NOT:
    AdapterTwo* a2 = dynamic_cast<AdapterTwo*>(adapter);
    if(a2)
        a2->setClientBroker("...");
    else
        // appropriate error handling
    

    您可以为 set/get Broker/ClientConnection 函数找到一个更通用的名称,让它们在 IAdapter 中已经是纯虚拟的,并在两个实现适配器类中覆盖它们,这样您就可以只需致电adapter-&gt;setXYZ("ampq");[编辑:根据您对问题的评论,在给定情况下不是选项]

    我个人最喜欢的是为您的 createList 函数提供一个附加参数,这样设置器就已经在工厂中被调用了 - 可能具有适当的默认值:空字符串,如果您选择 std::string 参数,或 @987654339 @ 在char const* 的情况下。如果参数与默认值不匹配,您只会调用 setter,当然...或者,您可以有两个重载。

    【讨论】:

      猜你喜欢
      • 2015-09-06
      • 2014-11-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-04-09
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多