3

I have one parent type

public class IObject{}

and can have a lot of sub-classes (even new ones in the future)

public class Object1 extends IObject{}
public class Object2 extends IObject{}
public class Object3 extends IObject{}
...
public class ObjectN extends IObject{}

Then based on the type of these objects I have to do different operations.

public class StrategyForObject1(){void do{}}
public class StrategyForObject2(){void do{}}
public class StrategyForObject3(){void do{}}
...
public class StrategyForObjectN(){void do{}}

So I want from my Context class:

   public Conext {
    IObject o;

    public void setObject(IObject o) {
        this.o = o;
    }

    void logic() {
        if (o instanceOf Object1) {
            new StrategyForObject1().do();
        }
        if (o instanceOf Object2) {
            new StrategyForObject2().do();
        }
        if (o instanceOf Object3) {
            new StrategyForObject3().do();
        }
        ...
        if (o instanceOf ObjectN) {
            new StrategyForObjectN().do();
        }
    }
}

So based on the type to execute different algorithms, but I want to be extensible like in Strategy pattern if I need to add new sub-class of IObject just to add new StrategyForObject**N** class, but not to change the Conext class. In Strategy pattern we have to specify the Strategy but here we have to do the opposite: to choose the strategy based on the type of the object. How to do that in Java in the best way?

Edit: The IObject can not be changed in order to add additional methods. I must separate logic from data,so it is not desirable to add implementation of the logic in Object1 class for example.

Andrejs
  • 10,803
  • 4
  • 43
  • 48
Xelian
  • 16,680
  • 25
  • 99
  • 152
  • This post may be useful for you : http://stackoverflow.com/questions/255214/when-should-i-use-the-visitor-design-pattern/35406737#35406737 – Ravindra babu Apr 17 '16 at 17:06

4 Answers4

6

I think you need to implement the visitor pattern. Basically for what you have it would look something like this:

interface IObjectVisitor {
    void visit(IObject1 obj1);
    void visit(IObject2 obj2);
    ...
    void visit(IObjectN objN);
}    
interface IObjectVisitable {
    void accept(IObjectVisitor visitor);
}
public abstract class IObject implements IObjectVisitable {
   ...
}
public class IObject1 extends IObject {
    public void accept(IObjectVisitor visitor) {
        visitor.visit(this);
    }
}
public class IObject2 extends IObject {
    public void accept(IObjectVisitor visitor) {
        visitor.visit(this);
    }
}
...
public class IObjectN extends IObject {
    public void accept(IObjectVisitor visitor) {
        visitor.visit(this);
    }
}
public class SomeLogicIObjectVisitor implements IObjectVisitor {
    void visit(IObject1 obj1) {
        //something with obj1
    }
    void visit(IObject2 obj2) {
        //something with obj2
    }
    ...
    void visit(IObjectN objN) {
        //something with objN
    }
}

Then you haven some logic to apply to some IObject like this:

public void someLogic(IObject obj) {
    SomeLogicIObjectVisitor visitor = new SomeLogicIObjectVisitor():
    visitor.visit(obj);
}

Object-Oriented wise, this is the best pattern you can implement. The reason is because it allows you for a modular and extensible approach, applying the right separation of concerns. Look at the answer provided by @nhouser9 for instance. While defining abstract void do(); in IObject seems to work at first glance, you would be embedding business logic inside your domain object, which most likely doesn't belong there. Also, if now you consider some other logic, lets call it, "logic2" now you have no option but to create abstract void do2(); on every IObject implementation, and continue to embed business logic there. With the visitor pattern, IObject implementations don't change, and you don't embed any logic inside the IObjects, simply just create a new visitor, Logic2IObjectVisitor and implement the logic of each IObject implementation there. And you'd call it like this:

public void someLogic2(IObject obj) {
    Logic2IObjectVisitor visitor = new Logic2IObjectVisitor():
    visitor.visit(obj);
}
Ulises
  • 9,115
  • 2
  • 30
  • 27
  • An explanation of when you would use a visitor pattern instead of simpler polymorphism would greatly help this answer. – user949300 Apr 16 '16 at 20:03
3

Have a look at the Visitor Pattern. I think its exactly what you are looking for.

Edit: To clarify:

import java.util.Arrays;
import java.util.List;

public class Test {
    public static abstract class IObject {
        public abstract void doSomeWork(StrategyVisitor strat);
    }

    public static class Object1 extends IObject {
        @Override
        public void doSomeWork(StrategyVisitor strat) {
            strat.doWork(this);
        }
    }

    public static class Object2 extends IObject {
        @Override
        public void doSomeWork(StrategyVisitor strat) {
            strat.doWork(this);
        }
    }

    public static class Object3 extends IObject {
        @Override
        public void doSomeWork(StrategyVisitor strat) {
            strat.doWork(this);
        }
    }

    public static interface StrategyVisitor {
        void doWork(Object1 o);

        void doWork(Object2 o);

        void doWork(Object3 o);
    }

    public static void main(String[] args) {
        List<IObject> objs = Arrays.asList(new Object1(), new Object2(), new Object3());

        StrategyVisitor visitor = new StrategyVisitor() {
            @Override
            public void doWork(Object1 o) {
                System.out.println("Object1");
            }

            @Override
            public void doWork(Object2 o) {
                System.out.println("Object2");
            }

            @Override
            public void doWork(Object3 o) {
                System.out.println("Object3");
            }
        };

        objs.stream().forEach(o -> o.doSomeWork(visitor));
    }
}

(See https://en.wikipedia.org/wiki/Visitor_pattern)

Skym0sh0
  • 305
  • 3
  • 6
2

First, your IObject class should be abstract, as it is only intended to be extended by other classes. Then you can declare a method inside it that must be overridden by classes which inherit from it, like this:

public abstract class IObject {
    abstract void do();
}

Then all of the classes that implement it must override that method with your custom logic:

public class Object1 extends IObject {
  @Override
  void do() {
    //custom logic
  }
}

In other words, you should be putting do() inside Object1 instead of StrategyForObject1.

This structure will allow you to call do() on a generic object of type IObject, as all children of IObject will implement the do() method. So in your logic method, you can just do this:

void logic(){
 o.do();
}
nhouser9
  • 6,730
  • 3
  • 21
  • 42
  • while this works, for a simple case, it has its disadvantages. Two main ones I see. First, you gotta embed business logic in your object. Second, you can only implement one kind of logic. The visitor pattern allows you for a modular and extensible approach – Ulises Apr 17 '16 at 12:00
  • @Ulises I agree that depending on the needs of the author, either your solution or mine could be the best. There are times and places for both. The visitor pattern answer got my upvote – nhouser9 Apr 17 '16 at 15:37
1

IObject could have an abstract method, do().

Then Context's logic() method just calls o.do().

This is a classic example of polymorphism.

user949300
  • 15,364
  • 7
  • 35
  • 66