Richard-Degenne
Richard-Degenne

Reputation: 2949

Java polymorphism: finding the right design pattern

Disclaimer: I know there are a lot of questions about polymorphism out there, but I couldn't find a suitable answer for my problem. If your Google-fu is better than mine, please forgive the dupe.

I have a model using inheritance, such as in the example below.

public abstract class Base {
// ...
}

public class ConcreteA extends Base {
    private String someString;
// ...
}

public class ConcreteB extends Base {
    private boolean someBool;
// ...
}

And I also have a List<Base>, which is composed of objects that are either ConcreteAs or ConcreteBs.

I need to generate a graphical view for each object in the list, but the resulting element is not the same for ConcreteAs and ConcreteBs. From the example above, the view for ConcreteA would be a text field, while the view for a ConcreteB would be a check box.

How can I achieve this using OO principles?

Upvotes: 2

Views: 537

Answers (4)

Ren&#233; Link
Ren&#233; Link

Reputation: 51333

The problem that you have is that you somewhere return a List<Base> when the caller must know the concrete type.

Usually this is caused because one tried to make a method more generic. E.g. if someone has this service methods

public List<ConcreteA> doSomethingA(){ ... }
public List<ConcreteB> doSomethingB(){ ... }

he might think it is a better idea to introduce a superclass, Base so that both methods can be substituted by

public List<Base> doSomething(){ ... }

This is a good idea if the caller is only interessted in a Base object. This means that ConcreateA and ConcreteB have some common behavior that the caller only depends on.

But in your case it seems that the caller needs the concrete type information that is not available anymore, because of the more generic method.

So you either must preserve or reconstruct the type information.

  1. Preserve the type by using a custom return type instead of making the method generic

     public class Result {
         private List<ConcreteA> concreteA;
         private List<ConcreteB> concreteA;
     }
    
     public Result doSomething();
    
  2. Recunstruct the type information using instanceof

  3. Reconstruct the type information by introcucing a visitor pattern.

Upvotes: 2

urag
urag

Reputation: 1258

In such cases, I usually use generics something like this

public abstract  class Base <T extends  Shape>{
public abstract T drawShape();

}

public class ConcreatA extends Base<Circle> {
@Override
public Circle drawShape() {
    return null;
}

}

public class ConcreatB extends Base<Square> {
@Override
public Square drawShape() {
    return null;
}

}

So now you can use list of Shapes

Upvotes: -1

Mickael
Mickael

Reputation: 4558

I think you're looking for Visitor Design Pattern.

From Wikipedia :

In object-oriented programming and software engineering, the visitor design pattern is a way of separating an algorithm from an object structure on which it operates. A practical result of this separation is the ability to add new operations to extant object structures without modifying the structures. It is one way to follow the open/closed principle.

In essence, the visitor allows adding new virtual functions to a family of classes, without modifying the classes. Instead, a visitor class is created that implements all of the appropriate specializations of the virtual function. The visitor takes the instance reference as input, and implements the goal through double dispatch.

Upvotes: 2

OldCurmudgeon
OldCurmudgeon

Reputation: 65793

Not a pattern - this is what abstraction is all about. Declare a method you want all subclasses of Base to implement and each must implement it in their own way.

Obviously you would pass parameters and/or get results of the methods.

public abstract class Base {
    abstract void graphicalView();
}

public class ConcreteA extends Base {
    @Override
    void graphicalView() {

    }
}

public class ConcreteB extends Base {
    @Override
    void graphicalView() {

    }
}

public void test() throws IOException {
    List<Base> bases = new ArrayList<>();
    for ( Base b : bases ) {
        b.graphicalView();
    }
}

Upvotes: 2

Related Questions