membersound
membersound

Reputation: 86935

Visitor Pattern or polymorphism?

I have a polymorphism structure in my project, and thinking of if it could be valuable to rewrite it to use Visitor Pattern.

The basic structure is: I have some drawing objects (Rectangle, Ellipse, Line, more to come) that have of course some behavior in common. The behavior is defined by the interface Drawable. So far, dragging should always behave the same, but drawing them selected should differ.

I therefore introduced the abstract Figure class, which already implements drag(), but delegates the implementation of drawSelected() to the extending classes.

Of course the interface will later be extended with additional functions I need, and also additional implementations of Figures will come.

Now my question: Would your rather stick to this design, or switch to Visitor. And if so, why? Especially I'm not sure if my present approach is good to have the logic/algorithms for drawing selections inside the objects itself.

interface Drawable {
    void drag();
    void drawSelected();
}


abstract class Figure implements Drawable {
    protected int x, y, w, h;

    @Override
    void drag() {
        //implementation always the same for different figures
    }
}


class Rectangle extends Figure {
    @Override
    drawSelected() {
        //draw a dashed rectangle around this object
    }
}

class Ellipse extends Figure {
    @Override
    drawSelected() {
        //draw a dashed ellipse around this object
    }
}

class Line extends Figure {
    @Override
    drawSelected() {
        //draw the line itself dashed
    }
}

Upvotes: 1

Views: 3235

Answers (4)

Kshitij
Kshitij

Reputation: 361

The problem you have requires using object structures that are unlikely to change over time; there may be new structures (figures) that may get added but a given object structure (figure) is unlikely to change. Though the structures might not change, you may add more and more operations on the structures and these operations may or may not be common across structures. I think these things make a use-case for using the Visitor pattern.

To elaborate further, consider the scenario where you are adding a new operation where you require one implementation for one group of figures, another implementation for another group and so on. In current design, you may be able to have only one common implementation in the abstract class (unless you use instanceOf, but that again is not considered a good practice) and would have to override that implementation in each of the other classes. Where as in case of using the Visitor pattern, you would always call the visit method (that would be bound at runtime) and within that you can decide which implementation to call.

In the current design, you may also reach a situation where you have common logic lying in different classes. But in Visitorpattern, you can maintain various implementations within the visitor class and reuse them in the visit method, so no code duplication.

Visitorpattern would also give you ease of switching a given figure from one implementation to the other, if required.

Though on adding new classes, you would need to add new implementations to the visitor interface, it will not impact any of you existing classes (figures) or their implementation.

Upvotes: 3

Heisenbug
Heisenbug

Reputation: 39204

I think the question is a bit overly-broad. We should know a little bit more about your application architecture, features and requirements. I'd just add another point of view to nikpon's one.

Consider the last point:

the classes defining the object structure rarely change, but you often want to define new operations over the structure. Changing the object structure classes requires redefining the interface to all visitors, which is potentially costly. If the object structure classes change often, then it's probably better to define the operations in those classes.

So the answer depends on what actually your figures data are and which operations they must implement. I'll give you an example where visitor patterns could be better:

  • A figure is only a collection of data that represent the actual figure (es. Circle: radius and center . Square side and center)
  • You have to draw figures across different systems/architectures where you are using different GUI/Rendering libraries

In a case like this there are 2 main consequences:

  • A figure class probably never changes, or it's changes can be hidden behind an interface that can never change. (es. A circle will always be defined by a center and a radius.)
  • You can't have a single method for drawing this objects since it changes accordingly to the used graphic library. If you put the drawing logic inside drawSelected(), than for every different graphic library you are using, you need to modify drawSelected implementation on each class(or use a suitable pattern to allow that).

In a case like this visitors benefits could be:

  • You can reuse the Figure classes across different platforms and graphic library, without any change
  • You can condense the drawing operations on a per[platform|library] concrete visitor classes. (es. DrawObjectsOnWindowsVisitor and DrawObjectsOnAndroidVisitor). If you need to support a different platform/library, you can simply add a visitor leaving figure classes untouched.

Btw this may not be the case, and eventually other pattern can be used to achieve these goals.

Upvotes: 1

Jordão
Jordão

Reputation: 56537

[...] and also additional implementations of Figures will come

I think that for this reason alone the visitor pattern is not appropriate for your scenario. Whenever you add a new class to the hierarchy, you'd have to change the visitor interface and all its implementations. The visitor is more appropriate when your hierarchy is stable and it allows you to add extra behaviors easily, just by creating new visitor implementations.

Take a look at the acyclic visitor for an alternative. But think hard about using these patterns, they can make the design more complex than it should be.

Upvotes: 1

user2173738
user2173738

Reputation:

Looking at the usages of the Visitor pattern that is from GoF seems that using that pattern in your case is inappropriate.

Usages

  • an object structure contains many classes of objects with differing interfaces, and you want to perform operations on these objects that depend on their concrete classes.
  • many distinct and unrelated operations need to be performed on objects in an object structure, and you want to avoid "polluting" their classes with these operations. Visitor lets you keep related operations together by defining them in one class. When the object structure is shared by many applications, use Visitor to put operations in just those applications that need them.
  • the classes defining the object structure rarely change, but you often want to define new operations over the structure. Changing the object structure classes requires redefining the interface to all visitors, which is potentially costly. If the object structure classes change often, then it's probably better to define the operations in those classes.

Upvotes: 1

Related Questions