简体   繁体   English

多态性的良好实践

[英]Good practice of polymorphism

Suppose a Domain which stores a pointer to Shape . 假设一个Domain存储了Shape的指针。 The exact shape ( Triangle or Rectangle ) is not known at compile time and would be clear after reading input. 确切的形状( TriangleRectangle )在编译时未知,并且在读取输入后会清楚。 At run-time I might need to access variables of derived structures but this is not possible because the pointer points to the base structure. 在运行时,我可能需要访问派生结构的变量,但这是不可能的,因为指针指向基本结构。 I found another solution which is to do "switch-on-type" but it is discouraged as pointed out in the answer here . 我发现了另一个解决方案是做“接通型”,而是在回答指出,它不鼓励在这里 He also said that 他还说

When you use polymorphism, you shouldn't need to care about what's behind a base class reference/pointer. 使用多态时,您无需关心基类引用/指针背后的内容。

Well in this case I do care so it sounds like I should not use polymorphism. 好吧,在这种情况下我确实在乎,所以听起来我不应该使用多态。 I guess what I do below is a bad design but then what is a good design to solve this problem? 我想我下面要做的是一个不好的设计,但是什么是解决这个问题的好的设计?

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

Your question clearly shows need for polymorphism, as you want to work with triangles, rechtangles, etc. and you know that all these are shapes. 您的问题清楚地表明需要多态,因为您想使用三角形,矩形等,并且您知道所有这些都是形状。

Why is t not advised to access specific data using a switch ? 为什么不建议使用交换机访问特定数据?

Because this is exactly the contrary of a polymorphic design. 因为这与多态设计完全相反。 Instead of working with shapes, drawing them, calculating their area, etc... you always need to know the type of the shape and code specific behavior. 而不是使用形状,绘制它们,计算它们的面积等……您始终需要了解形状的类型和代码特定的行为。

Imagine that you've finished your code, and then you discover that you want also squares and circles: what a nightmare it would be to maintain this. 想象一下,您已经完成了代码,然后发现您还想要正方形和圆形:维持这种状态将是一场噩梦。

How to solve this ? 如何解决呢?

You have to abstract from the concrete classes and define the general operations that you could perform on general shapes. 您必须从具体的类中抽象出来,并定义可以对常规形状执行的常规操作。 Then define these operations as virtual functions, and in your code using Domain, just call the virtual functions. 然后将这些操作定义为虚拟函数,并在使用Domain的代码中调用虚拟函数。

To genralize further, instead of creating objects from the class, you could use a factory method that returns a shape from a stream for example. 为了进一步进行泛化,您可以使用工厂方法从流中返回形状,而不是从类中创建对象。

Example: 例:

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

Note that working with smart pointers would be safer than with raw pointers; 注意,使用智能指针比使用原始指针更安全。

First. 第一。 Polymorphism is almost always only the right tool for the job when the concrete objects have exactly the same interface. 当具体对象具有完全相同的界面时,多态几乎总是仅是正确的工作工具。

Second, that's rarely the case and sometimes polymorphism is a better fit than a variant. 其次,情况很少,有时候多态性比变体更合适。

In such a situation, we defer the behaviour to the implementation rather than try to deduce it from outside the object. 在这种情况下,我们将行为推迟到实现中,而不是尝试从对象外部推断出来。

One way to do this is the 'visitor pattern': 一种方法是“访客模式”:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM