带参数化构造函数的抽象工厂

Tin*_*Tin 5 c++ polymorphism abstract-class design-patterns smart-pointers

我最近听说过抽象工厂模式,目前在需要参数化构造函数时如何设计这种模式存在一些疑问.

更确切地说,根据我的理解,这种设计模式的主要好处之一是促进代码管理,因为无论何时向系统引入新类型,都需要调整一个工厂函数,其中调用对象构造函数.

我发现的大多数示例都只考虑空构造函数(比如默认构造函数).
但是,如果需要使用参数化构造函数会发生什么?这种设计模式是否仍然有效?
由于参数在派生类之间的类型和数量可能不同,是否需要考虑几个工厂函数?下面我举例说明我想要实现的目标.请注意,为了减少代码行,我只考虑了每个类的一个构造函数,它们既可以作为默认构造函数,也可以作为参数化构造函数.

class Shape {
public:
    Shape(){std::cout << "Calling Shape Constructor\n";};
    virtual ~Shape(){std::cout << "Calling Shape Desstructor\n";};
    virtual void draw() const = 0;
    virtual void doSomething1(int) const = 0;
    virtual void doSomething2(int, float) const = 0;
};

class Rectangle : public Shape {
public:
    Rectangle(int l = 0, int b = 0 ):l(l),b(b){ std::cout << "Calling Rectangle Constructor\n"; };
    ~Rectangle(){ std::cout << "Calling Rectangle Destructor\n\n"; };
    virtual void draw() const{ /* Draw Rectangle */ }; 
    virtual void doSomething1(int) const { /* doSomething1 */};
    virtual void doSomething2(int, float) const { /* doSomething2 */};
private:
    int l,b;
};

class Circle : public Shape {
public:
    Circle(int r = 0):r(r){ std::cout << "Calling Circle Constructor\n"; };
    ~Circle(){ std::cout << "Calling Rectangle Destructor\n\n"; };
    virtual void draw() const{ /* Draw Circle*/ }; 
    virtual void doSomething1(int) const { /* doSomething1 */};
    virtual void doSomething2(int, float) const { /* doSomething2 */};
private:
    int r;
};

class ShapeFactory{

public:
    ShapeFactory(int = 0, double = 0);
    std::unique_ptr<Shape> CreateShape(const std::string & );
    ~ShapeFactory();
};

std::unique_ptr<Shape> ShapeFactory::CreateShape(const std::string & type /*, int rad, int side1, int side2, .... */) {

    if ( type == "circle" ) return std::unique_ptr<Shape>(new Circle( /* rad */)); // Should call Circle(int rad)!
    if ( type == "rectangle" ) return std::unique_ptr<Shape>(new Rectangle( /* side1, side2 */)); // Should call Rectangle(int, int)!
    // if ( type == "someNewShape") return std::unique_ptr<Shape>(new someNewShape( /* param1, param2, ... */)); // Should call someNewShape(param1, param2)!
    throw std::invalid_argument("MobileFactory: invalid type: " + type);
}
Run Code Online (Sandbox Code Playgroud)

我还有另一个疑问.想象一下,由于某些需要,我需要"ShapeFactory"类的类成员.我想直观地做的是:

std::vector< std::unique_ptr<ShapeFactory2> > mylist;
mylist.push_back( new ShapeFactory2(CreateShape("circle",radius), param1, param2) );
mylist.push_back( new ShapeFactory2(CreateShape("rectangle",side1,side2), param1, param2) );

for (std::vector< std::unique_ptr<ShapeFactory2> >::const_iterator it = v.begin(), end = v.end(); it != end; ++it)
{
    int param1   = it->param1;
    float param2 = it->param2;
    it->doSomething2(param1, param2);

    // or equivalently 
    Shape * myShape = *it;
    int param1   = it->param1;
    float param2 = it->param2;
    myShape->doSomething2(param1, param2);
}
Run Code Online (Sandbox Code Playgroud)

对于这种特定情况,'ShapeFactory'类声明将如何改变?我现在除了param1,param2之外还有一个smart_pointer作为类成员吗?如果是,是否有人可以说明如何实施构造函数/析构函数?

所有的建议/想法都非常受欢迎!;-)

thi*_*ton 2

仅当派生对象的签名足够相似以支持公共构造函数签名时,工厂模式才真正适用。这是一种常见情况,因为对象都适合共享虚拟函数签名,因此构造函数也类似。在您的示例中,从中心点和区域构造形状将适合工厂模式。

如果构造函数根本不相似,那么工厂模式就毫无意义,应该避免。当不使用工厂时,您提供的使用示例更加清晰和安全。

我将为您提供一个关于合理工厂的简短示例,其中包含我最近的代码中的参数:假设您想要将函数拟合到图像的像素。有两种可能的计算方式:不相交(行和列彼此分开)和联合。不相交拟合更便宜,但对于某些数据来说是不可能的。这些计算方式由两个不同的类(DisjointFitter 和 JointFitter)支持,它们接收数据作为参数,并且都派生自 Fitter。工厂的样子:

std::auto_ptr<Fitter> Fitter::create( const Data& data ) {
    if ( data.supports_disjoint_fitting() ) {
        return std::auto_ptr<Fitter>( new DisjointFitter(data) );
    } else {
        return std::auto_ptr<Fitter>( new JointFitter(data) );
    }
}
Run Code Online (Sandbox Code Playgroud)

从形状的角度来看,它可能看起来像:

enum BasicShape { Round, Edgy };
mylist.push_back( ShapeFactory::CreateShape( Round, 16 ) );
Run Code Online (Sandbox Code Playgroud)

抽象工厂方法如下所示:

static std::unique_ptr<Shape> CreateShape(BasicShape shape, double area) {
    if ( shape == Round ) 
        return std::unique_ptr<Shape>( new Circle( sqrt(area / M_PI) ) );
    else
        return std::unique_ptr<Shape>( new Square( sqrt(area) ) );
}
Run Code Online (Sandbox Code Playgroud)