所以,我的商业代码需要一些对象。
它不知道它需要多少对象,也不知道确切的类型(因为涉及多态性)。
对我来说,这听起来是一个很好的理由去工厂模式。
我的代码现在看起来像是:
std::vector<AbstractBaseClass*> objectList;
Factory f;
objectList = f.create("path/to/config.txt");工厂创建方法的原型如下所示:
std::vector<AbstractBaseClass*> Factory ::create(std::string configFile)好消息,起作用了!
工厂读取配置,然后决定要创建多少个对象以及它们具有哪些具体类型。
嗯,我做了很多搜索,但是我找不到一个工厂返回一个容器而不是一个对象的例子。因此,我的问题是:这种风格好吗?
我认为是的,因为这样的话,整个解析/创建过程就会隐藏在业务逻辑中。逻辑只知道它有一些带有一堆对象的容器。但也许你有其他意见?
请注意:这个项目都是为了学习良好的OOP习惯。
好的,想象一下这个方法是可以的。
我明白了,原始的指点是邪恶的。(好吧,根据定义,它们并不是邪恶的,但我尽量避免它们)。
所以,我想转到一些聪明的指点。在这台机器上缺少boost和C++11,我从auto_ptr开始。
好的,新的方法:
std::vector< auto_ptr<AbstractBaseClass> > objectList;
Factory f;
objectList = f.create("path/to/config.txt");和工厂:
std::vector< auto_ptr <AbstractBaseClass> > Factory ::create(std::string configFile)这看起来很邪恶。
它不编译,因为目前,我得到了一些疯狂的STL编译器错误。
但是,想象一下它是编译的。
这样好吗?
我从未见过这样的构造--一个工厂返回一个智能指针容器。因为我不是那个专家,我想问你对此有何看法。
与此相关的是,我应该使用什么智能指针?
业务逻辑是对象的唯一所有者,所以我猜是unique_ptr。但是,我不确定是否可以返回unique_ptr容器。我认为shared_ptr更容易实现。
发布于 2015-12-15 20:06:56
如果您使用C++作为一种主要面向对象的语言,您将不得不以某种形式处理指针。几乎所有指针问题的答案都是std::unique_ptr,因为它具有相当类似值的语义,同时仍然允许您使用多态性。它唯一的开销是语法混乱。这比
std::shared_ptr,因为这有额外的引用计数开销。std::auto_ptr是因为它有混淆的语义:复制的行为类似于移动,这也要求复制源不是const,这样就可以从源中删除移动的指针。(还请注意,std::vector的复制赋值操作符需要一个const引用,因此vector<auto_ptr<T>>是不可复制的。)unique_ptr字段来实现Pimpls,因为它负责所有必要的资源管理。但是,使用Pimpl作为桥模式可以使您能够使用基于值的API设计多态类层次结构,但使用类似指针的语义。特别是,您可以使用vector<BaseClass>作为更方便的vector<unique_ptr<BaseClassIf>>符号。
std::unique_ptr的“问题”是它需要C++11。这种指针类型的核心思想是它不能被复制,但是它可以被移动。因此,所有权总是明确界定的。当您按值返回一个对象时,它将受到复制语义的约束(即使实际的副本可能被优化了),但在C++11中,移动语义将允许您按值返回一个unique_ptr。
一般来说,使用一个智能指针容器是非常好的,并且比其他的方法更好。在您的示例中,由于C++03对auto_ptr的语义限制,它失败了。如果您不能升级到C++11 (当前所有主流编译器都支持它),那么您有两个现实的选择:使用原始指针,或者将指针包装在自定义的Pimpl中。如果我的努力是合理的,我会用Pimpl。这并不是非常复杂的代码,但是您必须小心地转发所有必要的操作:
class BaseClassIf {
public:
virtual ~BaseClassIf() {}
virtual void someOperation() = 0;
virtual BaseClassIf* copy() = 0; // { return new T(*this); }
}
class BaseClass {
BaseClassIf* impl;
void assertInvariant() { if (!impl) throw ...; }
public:
BaseClass(BaseClassIf* impl) : impl(impl) { assertInvariant(); }
~BaseClass() { if (impl) delete impl; impl = 0; }
BaseClass(const BaseClass& o) : impl(o.impl->copy()) { assertInvariant(); }
void someOperation() { impl->someOperation(); }
private:
BaseClass& operator=(const BaseClass&);
// Cannot be reasonably overloaded as a value-based copy,
// e.g. "*impl = *rhs.impl" since the exact types are unknown.
// Cannot be overloaded as a reference copy,
// e.g. "impl = rhs.impl" since that violates pointer ownership.
};注意,接口必须为访问复制构造函数做准备,因为BaseClass包装器不知道确切的类型。这是一种偶尔有用的技术(例如,用于隐藏模板参数的“类型擦除”),但在这里它只是使用多态带来的恼人的后果。还要注意的是,除非在接口中包含一个公共的virtual BaseClassIf::operator=(const BaseClassIf&)方法,否则不可能为适配器定义副本赋值操作符--但大多数对象层次结构无法从它们的公共基础上执行有用的副本。
发布于 2015-12-15 20:11:36
对于你的第一个问题:“让工厂返回容器是好的方式吗?”:
对于像std::vector<AbstractBaseClass*>这样的类型,IMHO没有什么特别之处--至少在原则上,它和任何其他类型一样是用户定义的类型。您可以制作一个typedef别名,如
typedef std::vector<AbstractBaseClass*> MyAbstractBaseClassContainer;如果这给您提供了更好的抽象,或者将其包装在一个新的类中。后者为您提供了将构造函数设置为私有的机会,因此禁止通过与静态类成员工厂方法不同的方法创建该类型的对象。但这实际上并不是“好”工厂所必需的--如果std::vector<AbstractBaseClass*>适合您的需要,请继续!
如果您更喜欢some_smart_ptr<AbstractBaseClass>而不是AbstractBaseClass*,特别是在vector的上下文中,这是一个与第一个完全独立的问题。既然这个问题已经有了很好的答案,例如这里或这里,或者@amon的答案,我不会试图回答这个问题,我怀疑我的答案会变得更好。
https://softwareengineering.stackexchange.com/questions/305144
复制相似问题