Let's say I've got a generic java class Filter<InputType, OutputType> which receives an input object and transforms it to an output object.

Now I've got two other classes (NoInputFilter<OutputType>, NoOutputFilter<InputType>) which should do the same except they haven't an InputType respectively an OutputType.

Now the three classes have some functionality in common. The Filter class should handle the input object the same way like the NoOutputFilter does, and it should handle the output object the same way like the NoInputFilter does.

So I've got something like this in terms of functionality, where B is Filter:

Three sets

Now, Java doesn't allow to inherit from multiple classes. Is there a design pattern to do something like this?

My first approach was to have two interfaces. One interface for the input, one interface for the output. The Filter class implements both. But this way I have to implement the interfaces two times each.

Then I thought about to give the Filter class (which implements the two interfaces) instances of NoOutputFilter and NoInputFilter and just call the right method instead of have duplicated code. But this breaks the semantics, has a lot of boilerplate code, and isn't a very clean approach in my opinion. Here a little demonstration:

class Filter<InputType, OutputType> implements INoOutputFilter<InputType>, INoInputFilter<OutputType> {
  private final INoOutputFilter<InputType> noOutputFilter;
  private final INoInputFilter<OutputType> noInputFilter;

  public void InputType setInput(InputType input) {
     noOutputFilter.setInput(input);
  }

  public OutputType getOutput() {
     noInputFilter.getOutput();
  }

  [...]
}

The last idea was to turn the class hierarchy up site down:

public class Filter<InputType, OutputType>
public class NoInputFilter<OutputType> extends Filter <Void, OutputType>
public class NoOutputFilter<InputType> extends Filter <InputType, Void>

This way, the classes behaves the right way, no duplicated code, but e.g. the NoOutputFilter has a public Void getOutput() method. Even though you can not instantiate something from Void, the API of those two classes are wrong. This would be something like this:

enter image description here

So this isn't a nice approach as well.

Do you have any hint how I could solve this in Java? Is there a design pattern for this?

有帮助吗?

解决方案

First of all, Java 8 introduced Default Methods, which is in fact multi-inheritance in Java.

History showed that complicated inheritance structure is cancer. You should favor composition over inheritance. In this case you are mixing input filter with output filter to create artificial bi-directional filter parent. Not clean for me at all.

I don't have your full picture, but from what you described, I would consider two interfaces: InputFilter and OutputFilter. Eventual Bi-directional filter implementation would implement both interfaces.

And if there would be some common functionality, I would extract that common logic into separate class and use it as dependency.

其他提示

I think your approach is wrong. You are looking at it in terms of classes instead of results. What you - in my opinion - should do is something like this:

  • Define an input interface
  • Define an output interface
  • Define a base class implementing both, with nullable protected properties
  • Define a concrete class for NoInput that exposes the output stuff
  • Define a concrete class for Filter that exposes both.

That's it.

A

Have you considered this?

public interface Input<InputType>   { ... }

public interface Output<OutputType> { ... }

public interface Filter<InputType,OutputType>
    extends Input<InputType>, Output<OutputType> { }

To give a more general-purpose answer to your question:

Multi-inheritance in Java

(Please note that this is somewhat experimental, and I have not ran it by other experienced java developers to see what they think about it. Perhaps I will receive some feedback here.)

The way to achieve multi-inheritance in Java is by means of interfaces. If you have class A extending classes X and Y then you need to begin by defining interfaces for X and Y, so that A can implement both interfaces. The question now becomes, once you have all this in place, how can you avoid code duplication. Prior to Java 8, this was a mess. With Java 8, this becomes considerably easier and cleaner. (Still not as clean as I would like it to be, but at least manageable.)

Whenever I create an interface in Java 8 which is bound to be implemented in various different ways, I add Decorator and Defaults sub-interfaces to it. So, I build something like this:

public interface Zork
{
    boolean foo();
    boolean bar( int a );

    interface Defaults extends Zork
    {
        @Override
        default boolean foo()
        {
            return bar( 0 );
        }
    }

    interface Decorator extends Defaults
    {
        Zork getDecoratedZork();

        @Override
        default boolean bar( int a )
        {
            Zork decoree = getDecoratedZork();
            return decoree.bar( a );
        }
    }
}

This way:

  • A class wishing to provide an implementation of Zork can be declared with implements Zork.Defaults and refrain from having to implement methods for which defaults have been provided.

  • A class wishing to act as a decorator of Zork can be declared with implements Zork.Decorator, supply an implementation for the getDecoratedZork(), and implement whatever subset of Zork is necessary, allowing the rest of the methods to be delegated to the decorated Zork.

  • A class wishing to multiply inherit Zork can be declared as follows:

(Note that since Zork is an interface, the MultipleInheritor class can implement many other interfaces like Zork.)

public class MultipleInheritor implements Zork.Decorator
{
    private final Zork myZork = new Zork.Defaults()
    {
        @Override
        public boolean bar( int a )
        {
            return a % 2 == 0;
        }
    }

    @Override
    public Zork getDecoratedZork()
    {
        return myZork;
    }
}
许可以下: CC-BY-SA归因
scroll top