How to make two classes which both have method with the same name, signature and return type behave like they would implement the same interface

luke :

I will explain my question on an example.

I have two classes NumberGeneratorApple and NumberGeneratorOrange. Both of them have method with the same signature and return type public Integer getNumber(). The problem is they do not implement the same interface althought it would be logical. I could make interface with this method and modify these classes to implement it but I don't want to do so. Here is why. These classes are generated automatically from let's say xml. In real example there are dozens of such classes. We have to generate them from time to time and it overwrites the old ones. I don't want to change each class manually to implement interface after each generation. Also it might not be obvious that it should be done for some other person working on the same project and even for me after some time.
Here is the first class:

public class NumberGeneratorApple {
    public Integer getNumber(){
        return 9;
    }
}  

and the second one:

public class NumberGeneratorOrange {
    public Integer getNumber(){
        return 19;
    }
}

Althought they don't implement the same interface I need to use them in a generic way. I just want to do something like this:

public class ClassThatOperates<T extends NumberGeneratorInterface> {
    T generator;

    public ClassThatOperates(T generator) {
        this.generator = generator;
    }

    public void doSomeOperation(){
        //Obviously it won't work for NumberGeneratorApple and NumberGeneratorOrange
        // because they do not implement NumberGeneratorInterface
        //And if I change "<T extends NumberGeneratorInterface>" to "<T>"
        // I cannot write method call "generator.getNumber()"
        System.out.print("The number with constant is: ");
        System.out.println(generator.getNumber() + 5);
    }
}

This is the interface (I know public is not necessary there and it is there by default. I just want to emphasize it):

public interface NumberGeneratorInterface {
    public Integer getNumber();
}

As you can see it is impossible to do such thing because neither NumberGeneratorApple nor NumberGeneratorOrange implements NumberGeneratorInterface. However I came up with some solution but following my instinct I think it is rather poor. I made wrapper classes:

public class NumberGeneratorAppleWrapper extends NumberGeneratorApple implements NumberGeneratorInterface {
}

and

public class NumberGeneratorOrangeWrapper extends NumberGeneratorOrange implements NumberGeneratorInterface {
}

It is a little bit tricky. It might be not obvious at first but when you call getNumber() on object of one of this classes you call in fact something like this:

@Override
public Integer getNumber() {
    return super.getNumber();
}

Now I can call it this way:

public class Main {
    public static void main(String[] args) {
        ClassThatOperates<NumberGeneratorAppleWrapper> classThatOperatesApple = new ClassThatOperates<>(new NumberGeneratorAppleWrapper());
        ClassThatOperates<NumberGeneratorOrangeWrapper> classThatOperatesOrange = new ClassThatOperates<>(new NumberGeneratorOrangeWrapper());
        classThatOperatesApple.doSomeOperation();
        classThatOperatesOrange.doSomeOperation();
    }
}

And I get the following output:

The number with constant is: 14
The number with constant is: 24

The advantage of such approach instead of adding manually implements NumberGeneratorInterface to each generated class is that We don't have to repeat the same work after each generation (which overrides old classes). We only have to add a new wrapper when generation results in some new, additional class.

I know that in such scenario I can get rid of generics in ClassThatOperates and just declare NumberGeneratorInterface generator; without <T extends...> and so on (and I even should) so the code would be simpler, but I want to make this example quite similar to what I found in some real project. I wrote: I have, I made, I came up etc. but in fact it is based on the already existing code that I found in some project.

There are my questions:
1. Is there a better solution?
2. Is this solution a so called "bad taste"?
3. If I have to use such solution maybe my whole approach is wrong?
4. If there is no better solution even if the whole approach is wrong what could be improved in this code (including getting rid of generics)?

rgettman :
  1. Is there a better solution?

Your solution looks good to me. You have used the Adapter Pattern, which uses existing functionality to conform to an otherwise unrelated interface. Without changing NumberGeneratorApple and NumberGeneratorOrange, you have adapted those classes' functionality to NumberGeneratorInterface.

More generally, you can have existing methods with different signatures be adapted to an interface, e.g. if you had

public class NumberGeneratorApple {
    public Integer getAppleNumber(){
        return 9;
    }
}

Then your adapter class would explicitly call getAppleNumber when implementing the interface.

public class NumberGeneratorAppleWrapper extends NumberGeneratorApple implements NumberGeneratorInterface {
    @Override
    public Integer getNumber() {
        return getAppleNumber();
    }
}
  1. Is this solution a so called "bad taste"?

This solution leaves no bad taste. The Adapter Pattern is a well established software design pattern.

  1. If I have to use such solution maybe my whole approach is wrong?

If you cannot change existing classes such as NumberGeneratorApple, then the Adapter Pattern is the way to go. If you can change them, then just have the classes implement the necessary interface directly.

public class NumberGeneratorApple implements NumberGeneratorInterface {
    @Override
    public Integer getNumber() {
        return 9;
    }
}

Or, if the method signature is different:

public class NumberGeneratorApple implements NumberGeneratorInterface {
    public Integer getAppleNumber() {
        return 9;
    }

    @Override
    public Integer getNumber() {
        return getAppleNumber();
    }
}
  1. If there is no better solution even if the whole approach is wrong what could be improved in this code (including getting rid of generics)?

If your classes such as NumberGeneratorApple really have exactly one method, and are not just simplifications of more complex classes with multiple methods for the purposes of this question, then you can use method references as another answer has hinted. Instead of declaring your own interface NumberGeneratorInterface, a method reference can be typed as a Supplier<Integer>.

public class ClassThatOperates {
    Supplier<Integer> generator;

    public ClassThatOperates(Supplier<Integer> generator) {
        this.generator = generator;
    }

    public void doSomeOperation(){
        System.out.print("The number with constant is: ");
        System.out.println(generator.get() + 5);
    }
}

Then you can use it as:

NumberGeneratorOrange ngo = new NumberGeneratorOrange();
ClassThatOperates cto = new ClassThatOperates(ngo::getNumber);
cto.doSomeOperation();

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=113728&siteId=1