多态性的良好实践

Good practice of polymorphism

本文关键字:多态性      更新时间:2023-10-16

假设Domain存储指向Shape的指针。确切的形状(TriangleRectangle)在编译时不知道,在读取输入后就会清楚了。在运行时,我可能需要访问派生结构的变量,但这是不可能的,因为指针指向基结构。我找到了另一种解决方案,那就是"开关式",但正如这里的答案所指出的那样,这是不鼓励的。他还说

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

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

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;
}

你的问题清楚地显示了多态性的需要,因为你想要与三角形,长方形等工作,你知道所有这些都是形状。

为什么不建议通过交换机访问特定的数据

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

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

如何解决这个问题

你必须从具体的类中抽象出来,并定义可以在一般形状上执行的一般操作。然后将这些操作定义为虚函数,在使用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;
}

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

第一。当具体对象具有完全相同的接口时,多态几乎总是最合适的工具。

第二,这种情况很少,有时多态性比变体更合适。

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

一种方法是使用"访问者模式":

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;
}