多态性的良好实践

时间:2016-08-23 21:27:34

标签: c++ pointers polymorphism

假设Domain存储指向Shape的指针。确切的形状(TriangleRectangle)在编译时是未知的,并且在读取输入后将会清楚。在运行时,我可能需要访问派生结构的变量,但这是不可能的,因为指针指向基础结构。我找到了另一个解决方案,即"开启类型"但正如答案here中所指出的那样,这是令人沮丧的。他还说过

  

当你使用多态时,你不应该关心基类引用/指针背后的内容。

在这种情况下,我确实关心所以听起来我不应该使用多态。我想我在下面做的是一个糟糕的设计,但那么解决这个问题的好设计是什么?

struct Shape
{
    int common_variable;
};

struct Triangle: Shape
{
    int triangle_specific_variable;
};

struct Rectangle: Shape
{
    int rectangle_specific_variable;
};

struct Domain
{
    Shape* shape;
};

int main()
{
    Domain domain;
    //domain.shape = new Triangle(); // depends on input.
    //domain.shape = new Rectangle(); // depends on input.

    return 0;
}

2 个答案:

答案 0 :(得分:6)

你的问题清楚地表明需要多态性,因为你想使用三角形,重新排列等等,你知道所有这些都是形状。

为什么不建议使用交换机访问特定数据?

因为这与多态设计完全相反。而不是使用形状,绘制它们,计算它们的面积等...你总是需要知道形状的类型和代码特定的行为。

想象一下,你已经完成了你的代码,然后你发现你也想要正方形和圆形:维持这个是多么噩梦。

如何解决这个问题?

您必须从具体类中抽象出来,并定义可以对常规形状执行的一般操作。然后将这些操作定义为虚函数,并在使用Domain的代码中,只需调用虚函数。

要进一步进行分类,您可以使用从流中返回形状的工厂方法,而不是从类中创建对象。

示例:

class Shape
{
public: 
    virtual void scale(double scale_factor)=0; 
    virtual void rotate(double angle)=0; 
    virtual void draw(window w)=0; 
    virtual Shape* clone()=0; 
    virtual ~Shape() {}
}; 

class Triangle: public Shape
{
    Point A, B, C; 
public: 
    Triangle (Point a,Point b, Point c) : A(a), B(b), C(c) { }
    void scale(double scale_factor) override; 
    void rotate(double angle) override; 
    void draw(window w) override; 
    Shape* clone() { return new Triangle(*this); }      
};

...

int main()
{
    Domain domain, domain2;
    Window wnd = CreateWindow(...);  // depends on your GUI library
    Point a,b,c; 
    cin >> a >> b >> c; 
    domain.shape = new Triangle(a,b,c); 

    // Once the object is constructed no need to know the details of the shape here
    domain.shape->rotate(45); 
    domain2.shape = domain.shape->clone(); 
    domain.shape->draw(wnd); 
    ...
    return 0;
}

请注意,使用智能指针比使用原始指针更安全;

答案 1 :(得分:0)

首先。当具体对象具有完全相同的界面时,多态性几乎总是只是工作的正确工具。

其次,情况很少,有时多态性比变体更适合。

在这种情况下,我们将行为推迟到实现,而不是试图从对象外部推断出它。

这样做的一种方法是访问者模式':

struct triangly_thing { int i; };
struct rectangly_thing { int i; };

struct shape_visitor
{
    virtual void operator()(triangly_thing thing) const
    {
        // do triangly things
    }

    virtual void operator()(rectangly_thing thing) const
    {
        // do rectangly things
    }
};

struct Shape
{
    int common_variable;

    virtual void visit(shape_visitor const& visitor)
    {

    }
};


struct Triangle: Shape
{
    triangly_thing my_triangly_thing;

    void visit(shape_visitor const& visitor) override
    {
        visitor(my_triangly_thing);
    }
};

struct Rectangle: Shape
{
    rectangly_thing my_rectangly_thing;

    void visit(shape_visitor const& visitor) override
    {
        visitor(my_rectangly_thing);
    }
};

struct Domain
{
    Shape* shape;
};

int main()
{
    Domain domain;

    struct my_shape_visitor_type : shape_visitor
    {
        // customise overrides in here
    } my_shape_visitor;

    domain.shape->visit(my_shape_visitor);

    return 0;
}