Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Visitor pattern implementation in case of source code un-availability

One of the reasons to consider the Visitor_pattern:

A practical result of this separation is the ability to add new operations to existing object structures without modifying those structures.

Assume that you don't have the source code of third party libraries and you have added one operation on related objects.

Since you don't have object, your elements (Third party classes) can't be modified to add Visitor.

enter image description here

In this case, double dispatch is not possible.

So which option is generally preferred?

Option 1: Extend one more inheritance hierarchy on top of third party class and implement pattern as show in picture with double dispatch?

For a given hierarchy of Class B which extends Class A, I will add

ElementA extends A
ElementB extends B

Now ConcreteElements are derived from ElementA instead of class A.

Cons: The number of classes will grow.

Option 2: Use Visitor class a central helper class and get the work done with single dispatch.

Cons: We are not really following Visitor patter as per UML diagram.

Correct if I am wrong.

like image 747
Aditya W Avatar asked Aug 12 '16 14:08

Aditya W


People also ask

When should we use visitor design pattern?

Visitor pattern is used when we have to perform an operation on a group of similar kind of Objects. With the help of visitor pattern, we can move the operational logic from the objects to another class. For example, think of a Shopping cart where we can add different type of items (Elements).

What is the purpose of visitor pattern in design pattern?

The purpose of a Visitor pattern is to define a new operation without introducing the modifications to an existing object structure.

What is visitor pattern in Java?

Visitor is a behavioral design pattern that allows adding new behaviors to existing class hierarchy without altering any existing code.

What is visitor pattern python?

Visitor Method is a Behavioral Design Pattern which allows us to separate the algorithm from an object structure on which it operates. It helps us to add new features to an existing class hierarchy dynamically without changing it.


Video Answer


1 Answers

You could combine a Wrapper and Visitor to solve your problems. Using the wrapper to add a visit method allows you to increase the usability of these objects. Of course you get the full advantages (less dependency on the legacy classes) and disadvantages (additional objects) of a wrapper.


Here's a worked-up example in JAVA (because it is pretty strict, does not do double-dispatch by itself, and I'm quite familiar with it):

1) Your legacy Objects

Assuming you have your legacy objects Legacy1 and Legacy2which you cannot change, which have specific business methods:

public final class Legacy1 {
    public void someBusinessMethod1(){
        ...
    }
}

and

public final class Legacy2 {
    public void anotherBusinessMethod(){
        ...
    }
}

2) Prepare the Wrapper

You just wrap them in a VisitableWrapper which has a visit method that takes your visitor, like:

public interface VisitableWrapper {
    public void accept(Visitor visitor);
}

With the following implementations:

public class Legacy1Wrapper implements VisitableWrapper {

    private final Legacy1 legacyObj;

    public Legacy1Wrapper(Legacy1 original){
        this.legacyObj = original;
    }

    public void accept(Visitor visitor){
         visitor.visit(legacyObj);
    }
}

and

public class Legacy2Wrapper implements VisitableWrapper {

    private final Legacy2 legacyObj;

    public Legacy2Wrapper(Legacy2 original){
        this.legacyObj = original;
    }

    public void accept(Visitor visitor){
         visitor.visit(legacyObj);
    }
}

3) Visitor, at the ready!

Then your own Visitors can be set to visit the wrapper like so:

public interface Visitor {
     public void visit(Legacy1 leg);
     public void visit(Legacy2 leg);
}

With an implementation like so:

public class SomeLegacyVisitor{

    public void visit(Legacy1 leg){
        System.out.println("This is a Legacy1! let's do something with it!");
        leg.someBusinessMethod1();
    }

    public void visit(Legacy2 leg){
        System.out.println("Hum, this is a Legacy 2 object. Well, let's do something else.");
        leg.anotherBusinessMethod();
    }
}

4) Unleash the power

Finally in your code, this framework would work like this:

public class TestClass{
    // Start off with some legacy objects
    Legacy1 leg1 = ...
    Legacy2 leg2 = ...

    // Wrap all your legacy objects into a List:
    List<VisitableWrapper> visitableLegacys = new ArrayList<>();
    visitableLegacys.add(new Legacy1Wrapper(legacy1));
    visitableLegacys.add(new Legacy2Wrapper(legacy2));

    // Use any of your visitor implementations!
    Visitor visitor = new SomeLegacyVisitor();
    for(VisitableWrapper wrappedLegacy: visitableLegacys){
        wrappedLegacy.accept(visitor);
    }
}

The expected output:

This is a Legacy1! let's do something with it!
Hum, this is a Legacy 2 object. Well, let's do something else.

Drawbacks:

  1. Quite a lot of boilerplate. Use Lombok if you develop in Java.
  2. Quite a lot of wrapper objects instances. May or may not be a problem for you.
  3. You need to know the specific type of the objects beforehand. This implies you know their subtype, they aren't bundles in a List. If that's the case, you have no other option but to use reflection.
like image 188
MrBrushy Avatar answered Nov 15 '22 18:11

MrBrushy