C++: doubts about visitor pattern

前端 未结 5 1568
天命终不由人
天命终不由人 2021-02-08 21:23

I know what Visitor Pattern is and how to use it; this question is not a duplicate of this one.


I\'ve got a library where I put most of the reusable code I write,

相关标签:
5条回答
  • 2021-02-08 21:26

    So there's a class xxxShape, which in some way contains information that "drives" the rendering. For Circles that might be centre, radius, for Squares some corner coordinates or some such. Maybe some other stuff about fillings and colours.

    You dont't want to/cannot update those classes to add the actual rendering logic, and I think your reasons for not doing so are valid/inevitable.

    But presumably, you have enough public access methods on the classes to allow you to get at the "driving" information, otherwise you are doomed.

    So in which case why can you not just wrap these items:

     CircleRenderer hasA Cicle, knows how to render Circles
    

    and so on. Now use the Visitor pattern across the Renderer classes.

    0 讨论(0)
  • 2021-02-08 21:32

    There a many possible solutions, but you could do this, for example: Start new hierarchy, which renders Shapes in a specific Context:

    // contracts:
    
    class RenderingContext {
    public: virtual void DrawLine(const Point&, const Point&) = 0; 
        // and so on...
    };
    
    class ShapeRenderer {
    public: virtual void Render(RenderingContext&) = 0;
    };
    
    // implementations:
    
    class RectangleRenderer : public ShapeRenderer {
     Rectangle& mR;
    
    public: 
     virtual void Render(RenderingContext& pContext) {
       pContext.DrawLine(mR.GetLeftLower(), mR.GetRightLower());
       // and so on...
     }
    
     RectangleRenderer(Rectangle& pR) : mR(pR) {}
    };
    
    0 讨论(0)
  • 2021-02-08 21:35

    This doesn't look like a case for the Visitor pattern to me.

    I would suggest you have a RenderableShape class that aggregates a Shape object, then create subclasses for each shape. RenderableShape would have a virtual render method.

    If you want to support multiple rendering engines, you can have a RenderContext base class that abstracts drawing operations, with subclasses for each rendering engine, each subclass implementing the drawing operations in terms of its rendering engine. You then have RenderableShape::render take a RenderContext as an argument, and draw to it using its abstracted API.

    0 讨论(0)
  • 2021-02-08 21:36

    First: "Visitor Pattern is a way to simulate Double Dispatching in C++." This is, erm, not fully right. Actually, double dispatch is one form of multiple dispatch, which is a way to simulate (the missing) multi-methods in C++.


    Whether operations on a class hierarchy should be implemented by adding virtual functions or by adding visitors is determined by the probabilities of adding classes vs. adding operations:

    • If the number of classes keeps changing more rapidly than the number of operations, use virtual functions. That is because adding a class requires modifying all visitors.
    • If the number of classes is relatively stable compared to the number of operations, use visitors. That is because adding a virtual function requires changing all classes in the hierarchy.

    Yes, many libraries do not come with a visitor interface.
    When we are just looking at the reasoning above, this would be right if the number of classes changes often. That is, if a library is released often, with new classes being added constantly, then providing a visitor interface wouldn't make much sense, because each time a new release brings new classes, everybody using the library need to adapt all of their visitors. So if we only looked at the above reasoning, a visitor interface would seem to only be helpful if the number of classes in a lib's class hierarchy seldom or never changes.

    However, with 3rd-party libraries there's another aspect to that: Usually, users cannot change the classes in the library. That is, if they need to add an operation, the only way they can do this is by adding a visitor - if the library provides the hooks for them to plug into it.
    So if you are writing a library and feel like users should be able to add operations to it, then you need to provide a way for them to plug their visitors into your lib.

    0 讨论(0)
  • 2021-02-08 21:41

    I understand absolutely what you said and I share the same concerns. The problem is that the Visitor Pattern is not very clearly defined and the original solution for it is misleading, IMHO. This is why there are so many variations of this pattern.

    In particular, I believe that the correct implementation should support legacy code, I mean: a binary you've lost the source code at all, isn't it? This is what the definition says: that you should never had to change the original data structures.

    I don't like implementations with visitA, visitB, visitWhatever, acceptA, acceptB, acceptWhatever. This is absolutely wrong, IMHO.

    If you have a chance, please have a look at an article I've written about this.

    It's Java, but you can port to C++ easily if you find it useful for your purposes.

    I hope it helps

    Cheers

    0 讨论(0)
提交回复
热议问题