0

TLDR: I have three subclasses, each inherits from the same parent class, each defines an identical method that does almost the same thing, except that each of these methods has a different return type. The return type does not affect the logic of the method in any way. To get rid of this repetition, I want to be able to define a single method in the parent class that each of the child classes can use - but how, when I need this method to be able to return three different types?

Background: I have three different GUI components - each of them is designed to display information about a different algorithm, and each of them has a "RunAlgorithm" button and a "LaunchAnimation" button (and identically named controller methods). An EgGivenData object can be passed into the components, and in each component, depending on fields in the EgGivenData object, the displayed information varies and buttons are either activated or greyed out.

The RunAlgorithm and LaunchAnimation methods are called on the concrete types in response to ActionEvents when the button of the associated component is clicked.

Each of these GUI Components is associated with a different algorithm, but the components mostly do the same thing and share a lot of logic, so each component inherits from the same abstract class. The purpose of the algorithms is to convert the EgGivenData into animation data - each algorithm does this in a slightly different way. The LaunchAnimation button in each component passes the data outputted by the algorithm into an "AnimateResults" method.

The Problem:

My problem is this: each of the algorithms returns an OutputData array of a different data type. Therefore, even though the "LaunchAnimation" method does pretty much the same thing for each component, I've had to implement it separately for each component, each time with a different return type.

My initial solution was this: just encapsulate the three different OutputData arrays into three different types, each of which inherits from the same interface. The LaunchAnimation method can then have this interface as its return type, and the AnimateResults method can take the interface as a parameter, and act on each sub-type differently using polymorphism.

So I started making the interface, but then I realised it would need getData and setData methods, so I will need to specify the return type for the getData method in the interface. However, I need each class which implements the interface to have a getData method with a different return type. So the only solution is to create an empty interface, each of the subclasses will have to define their own getData and setData methods, and this will allow me to pass the encapsulated data from one method to another. But using an empty interface seems like bad programming.

(I'm using Java, following an Model-View-Controller architecture in JavaFX. When I say component, I mean an FXML view with an associated controller. I'm currently trying to implement the model, leading into the problems outlined above.)

4
  • If I explained poorly or you need clarification please let me know! Thanks! Commented Feb 16 at 14:08
  • 3
    Can you edit your question to include more information about that consuming code? Are callers using the concrete types or the base types? Commented Feb 16 at 17:04
  • 2
    It is much easier to answer this question if you provide some minimal (pseudo-) code that demonstrates the problem. Java uses generics to provide different results. For example java.util.List<E> has a method E get(int index) where <E> is the different returntype
    – k3b
    Commented Feb 20 at 10:55
  • This seemed like an obvious candidate for generics until I noticed that you are talking about arrays. Is the use of arrays an absolute requirement? Can you use a List instead?
    – JimmyJames
    Commented Feb 20 at 15:18

4 Answers 4

2

As I see it, you have two possible solutions for this. Generics and the Visitor pattern. Personally, I would go with the second approach as I find it cleaner and more maintainable.

In the first approach utilizing generics, you define a common interface or abstract class for your different return types. Each subclass then implements this interface and provides its specific implementation. You declare a generic method in your parent class that returns the common interface, allowing each subclass to override this method and specify its concrete return type.

e.g.

interface OutputData {
    // Common interface methods
}

class OutputData1 implements OutputData {
    // Implementation for OutputData1
}

class OutputData2 implements OutputData {
    // Implementation for OutputData2
}

abstract class ParentClass {
    public abstract <T extends OutputData> T launchAnimation();
}

class SubClass1 extends ParentClass {
    @Override
    public OutputData1 launchAnimation() {
        // Implementation for launching animation and returning OutputData1
    }
}

class SubClass2 extends ParentClass {
    @Override
    public OutputData2 launchAnimation() {
        // Implementation for launching animation and returning OutputData2
    }
}

In the second approach using the Visitor pattern, you define a visitor interface with visit methods for each of your concrete types. Your OutputData types implement an accept method, which takes a visitor as an argument and calls the appropriate visit method based on their type. In your parent class, you define a method that accepts a visitor, and each subclass implements this method by passing itself to the visitor.

e.g.

interface OutputData {
    void accept(OutputDataVisitor visitor);
}

class OutputData1 implements OutputData {
    @Override
    public void accept(OutputDataVisitor visitor) {
        visitor.visit(this);
    }
}

class OutputData2 implements OutputData {
    @Override
    public void accept(OutputDataVisitor visitor) {
        visitor.visit(this);
    }
}

interface OutputDataVisitor {
    void visit(OutputData1 data);
    void visit(OutputData2 data);
}

abstract class ParentClass {
    public abstract void launchAnimation(OutputDataVisitor visitor);
}

class SubClass1 extends ParentClass {
    @Override
    public void launchAnimation(OutputDataVisitor visitor) {
        // Any additional logic before visiting...
        new OutputData1().accept(visitor);
        // Any additional logic after visiting...
    }
}
1

I would implement it using generics similar to java.util.List<E> which has a method E get(int index) where <E> is the different returntype.

Taking the minimal example code from Sebastan-s answer it would look like

interface OutputData {
    // Common interface methods
}

class OutputData1 implements OutputData {
    // Implementation for OutputData1
}

class OutputData2 implements OutputData {
    // Implementation for OutputData2
}

class ParentClass<T extends OutputData>  {
    public T launchAnimation() {...}
}

class SubClass1 extends ParentClass<OutputData1> {...}

class SubClass2 extends ParentClass<OutputData2> {...}
1

three subclasses, each inherits from the same parent class

That's polymorphism used for inheritance and it is... what you called it?

bad programming

To use a method in multiple places the method has to be defined in a class accessible to the classes that needs to use it (e.g. a util class, although using util classes it is controversial it is the simplest terminology I can think of to describe the concept of reusing a method).

each defines an identical method that does almost the same thing, except that each of these methods has a different return type

then add an extra level of indirection implementing decorator design pattern.

I want to be able to define a single method in the parent class that each of the child classes can use

avoid it, it is polymorphism for inheritance, instead develop the common method in a class and have three decorator implementations calling the common method and build different return type.

0

You can try writing a plain old function that does 95% of the work, identical for all three classes. And then each class gets a tiny instance method that mostly calls the plain old function.

You could have an instance method in the base class. Because it is incomplete and you don't want it to be called except from the subclasses, you would make it protected in C++. You could give it another name, so you are sure nobody else calls it. If you don't want identical code but almost identical code, it could have an additional argument to choose which behaviour to implement.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.