【问题标题】:Factory pattern with private constructors in C++C++ 中带有私有构造函数的工厂模式
【发布时间】:2015-11-27 20:26:57
【问题描述】:

我正在尝试实现一个由

组成的工厂模式
  • 工厂类
  • 具有受保护构造函数的抽象类
  • 具有私有构造函数和虚拟公共的继承类 析构函数。

我想确认一下

  • 除了工厂之外没有其他人不能创建任何实例
  • 如果定义了新的继承类,则不需要对接口类和已定义的继承类进行任何修改。突出新的类实现并添加到工厂类中的创建方法。

我也不想为每个继承的类编写类似的代码(如每个初始化的静态工厂方法),并让未来的开发人员为工厂连接做很多工作。

即使用伪代码

class Factory;

class Interface
{
    protected:
        Interface(){/*Do something*/};
    public:
        virtual ~Interface(){/*Do something*/}
    /*I wish I could do below and it is valid for all inherited 
    classes but friendship is not inherited in C++*/
    //friend Interface* Factory::create(Type)
};

class InheritedA:public Interface
{
    private:
        InheritedA(){/*Do something*/};
    public:
        virtual ~InheritedA(){/*Do something*/}
    /*I dont want to do below two lines for every inherited class*/
    //friend Interface Factory::create(Type)
    //public: Interface* factoryInheritedA(){return new InheritedA();}
};

class InheritedB:public Interface
{
    private:
        InheritedB(){/*Do something*/};
    public:
        virtual ~InheritedA(){/*Do something*/}
};

class Factory
{
     static Interface* create(Interface type)       
     {
           switch(type)
           {
           case A:
                return new InheritedA();
           case B:
                return new InheritedB();
           default:
                //exceptions etc
           }
     }
}

int main()
{
    Interface* I = Factory::create(A/*or B*/);
    return 0;
}

上面的代码是我放出来的最接近的。欢迎提出任何建议(C++ 的专长、不同的设计……)。

【问题讨论】:

  • 你忘记写问题了。
  • 你为什么需要这一切?听起来像爪哇。这可能是 XY 问题的情况。
  • @UzorTuTuEjt 我写了我想对这些限制做些什么。我真的不明白为什么需要问句
  • 一个问题是必需的,我看不出问题是什么,为什么你需要这个或者你想解决什么
  • @TonyD 在建议迁移之前请阅读网站的help center。这是伪代码,因此与 codereview 无关。

标签: c++ design-patterns factory-pattern


【解决方案1】:

我认为这不是一个好主意,但这里有一种方法可以做到这一点。您创建一个只能由 Factory 创建的 Tag 类型,并使所有构造函数都采用该类型的参数。

class Factory;

class Tag
{
    Tag() {}
    friend Factory;
};

class Interface 
{
public:
    Interface(Tag t) {}
    virtual ~Interface() {}
};

struct Impl1: public Interface
{
    Impl1(Tag t): Interface(t) {}
};

class Factory
{
public:
   Interface* makeInstance()
   {
       return new Impl1( Tag{} );
   }
};

void foo()
{
    Impl1 i( Tag{} );
}

您将在foo() 中收到编译器错误,因为Tag::Tag 是私有的。

【讨论】:

  • 至少它禁止其他人创建实例。这还不是一个解决方案,但可能是一个好的开始。谢谢。
【解决方案2】:

你可以有一个模板函数:

template<typename Type>
std::unique_ptr<Interface> make_interface() {
    // exceptions etc..
}

template<>
std::unique_ptr<Interface> make_interface<InheritedA>() {
    return std::make_unique<InheritedA>();
}

template<>
std::unique_ptr<Interface> make_interface<InheritedB>() {
    return std::make_unique<InheritedB>();
}

但我真的看不出所有这些 Javaesque 样板的意义所在。更不用说您正在无缘无故地将编译时信息(类型)转换为运行时信息(通过异常)。

我会选择:

std::unique_ptr<Interface> ptr_a = std::make_unique<InheritedA>();
std::unique_ptr<Interface> ptr_b = std::make_unique<InheritedB>();

在需要时。

【讨论】:

  • 模板特化也可以用两个常规函数代替,对吧?
  • @JorenHeit 好吧,不,如果你想让make_interface&lt;Type&gt;Type 不是InheritedAInheritedB 时抛出异常。
  • 你为什么想要那个?我宁愿有一个编译器错误。
  • 他在哪里说明了这个要求?我意识到他的伪代码会产生异常,但这不一样。让我感到奇怪的是,模板用于在您的解决方案中生成运行时错误,仅此而已。闻起来像模板过度使用。
  • @JorenHeit 在我看来,模板和编译时间信息使用得不够充分。 OP 在他的原始帖子中,要求提供一种在运行时区分类型的方法,如果传递了不正确的类型,最终会引发异常。这就是我的代码正在做的事情,但正如我在答案中描述的那样,这不是很有用。
【解决方案3】:

使用工厂很少是一个好习惯。我把它和 Singleton 一起算作反模式。在好的设计中,类不关心它们是如何创建的。在您的情况下,当在工厂中使用时,您如何使用自定义分配器创建您的类?在堆栈上?在共享内存中?在内存映射文件中?从缓冲区?到位?这在 Factory 中真的很难涵盖,但不要绝望 - 简单而优雅的解决方案是抛弃工厂!

【讨论】:

  • 将工厂视为反模式可能是一种非常罕见的观点。我也无法遵循推理。据我了解,工厂的目的是将客户端与相关对象的创建分离,即客户端不应该知道创建了什么,或者它是如何创建的。它与创建的对象本身无关。正是为了强制“类不关心它们是如何创建的”,你认为这是好的设计。
  • @Jens,首先,“客户不应该知道它是如何创建的”这句话是糟糕设计的同义词。客户端需要能够控制对象的创建方式。一切都只是简单地在堆中更新的日子已经一去不复返了。例如,没有一个对性能要求严格的应用程序(并且对使用 C++ 进行非性能要求的应用程序的任何人感到遗憾)真的愿意使用 vanilla new 创建他们的对象。就如此容易。您需要为工厂提供分配器,这是必须的。通讯。篇幅限制了我进一步的写作,但我坚定不移——工厂的生产方式通常很糟糕。
  • 我不认为你明白我的意思。假设我有一个类 A,它使用带有抽象接口 B 和子类 C 和 D 的类层次结构。在类 A 中,为了可重用,我希望能够自定义 C 和 D 中的哪一个(或创建了一个新的实现 X)。如果我使用工厂,我可以做到这一点。我还可以通过实施另一个工厂来决定分配策略。 A 类不知道如何创建以及创建什么。我认为这是一个很好的设计,大多数人可能都同意。我也不知道你如何编写没有工厂的可测试代码。
  • @Jens,我迷失了你的榜样。如果您按照原始 OP 示例进行操作,那么工厂在那里绝对没用。
  • 原始示例没有提供足够的上下文进行任何判断。他只展示了一个抽象接口、它的两个实现和一个工厂,并询问如何防止工厂以外的其他类创建对象。你也可以说在那里使用接口是绝对没用的,就像使用类一样。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-07-24
  • 1970-01-01
  • 2014-11-10
相关资源
最近更新 更多