Multi ereditarietà in Java

0

Diciamo che ho una classe java generica Filter<InputType, OutputType> che riceve un oggetto di input e lo trasforma in un oggetto di output.

Ora ho due altre classi ( NoInputFilter<OutputType> , NoOutputFilter<InputType> ) che dovrebbero fare lo stesso eccetto che non hanno un InputType rispettivamente un OutputType .

Ora le tre classi hanno alcune funzionalità in comune. La classe Filter dovrebbe gestire l'oggetto di input nello stesso modo come fa NoOutputFilter , e dovrebbe gestire l'oggetto di output nello stesso modo come fa NoInputFilter .

Quindi ho qualcosa di simile in termini di funzionalità, dove B è Filter :

Ora, Java non consente di ereditare da più classi. C'è un modello di progettazione per fare qualcosa di simile?

Il mio primo approccio era di avere due interfacce. Un'interfaccia per l'input, un'interfaccia per l'output. La classe Filter implementa entrambi. Ma in questo modo devo implementare le interfacce due volte ciascuna.

Poi ho pensato di dare le istanze Filter class (che implementa le due interfacce) di NoOutputFilter e NoInputFilter e basta chiamare il metodo giusto invece di avere il codice duplicato. Ma questo rompe la semantica, ha un sacco di codice standard, e non è un approccio molto pulito a mio parere. Ecco una piccola dimostrazione:

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();
  }

  [...]
}

L'ultima idea era di abbassare la gerarchia delle classi sul sito:

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

In questo modo, le classi si comportano nel modo giusto, nessun codice duplicato, ma ad es. NoOutputFilter ha un metodo public Void getOutput() . Anche se non puoi creare un'istanza di qualcosa da Void , l'API di queste due classi è sbagliata. Questo sarebbe qualcosa del genere:

Quindi questo non è un buon approccio.

Hai qualche suggerimento su come potrei risolvere questo in Java? C'è un modello di design per questo?

    
posta Obenland 19.09.2015 - 20:36
fonte

3 risposte

0

Prima di tutto, Java 8 ha introdotto Metodi predefiniti , che è in effetti multi-ereditarietà in Java.

La storia ha dimostrato che la complicata struttura ereditaria è il cancro. Devi favorire la composizione sull'ereditarietà . In questo caso si sta mescolando il filtro di input con il filtro di output per creare un filtro genitore bidirezionale artificiale. Non è affatto pulito per me.

Non ho la tua immagine completa, ma da quello che hai descritto, considererei due interfacce: InputFilter e OutputFilter . L'eventuale implementazione di filtri bidirezionali implementerebbe entrambe le interfacce.

E se ci fosse qualche funzionalità comune, estrarrei quella logica comune in una classe separata e usarla come dipendenza.

    
risposta data 19.09.2015 - 20:47
fonte
1

Penso che il tuo approccio sia sbagliato. Lo stai guardando in termini di classi anziché di risultati. Quello che tu, a mio avviso, dovrebbe fare è qualcosa del genere:

  • Definisci un'interfaccia di input
  • Definisci un'interfaccia di output
  • Definisci una classe base che implementa entrambi, con proprietà protette nullable
  • Definisci una classe concreta per NoInput che espone la roba di output
  • Definisci una classe concreta per Filtro che espone entrambi.

Questo è tutto.

A

    
risposta data 19.09.2015 - 21:48
fonte
0

Hai preso in considerazione questo?

public interface Input<InputType>   { ... }

public interface Output<OutputType> { ... }

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

Per dare una risposta più generale alla tua domanda:

Multi-ereditarietà in Java

(Si noti che questo è un po 'sperimentale, e non ho eseguito da altri sviluppatori esperti di Java per vedere cosa ne pensano. Forse riceverò qualche feedback qui.)

Il modo per ottenere multi-ereditarietà in Java è tramite interfacce. Se hai classi di estensione X e Y di classe A, allora devi iniziare definendo le interfacce per X e Y, in modo che A possa implementare entrambe le interfacce. La domanda diventa ora, una volta che hai tutto questo a posto, come puoi evitare la duplicazione del codice. Prima di Java 8, questo era un disastro. Con Java 8, questo diventa notevolmente più facile e più pulito. (Non è ancora pulito come vorrei, ma almeno gestibile.)

Ogni volta che creo un'interfaccia in Java 8 che è destinata a essere implementata in vari modi, aggiungo Decorator e Defaults sotto-interfacce ad essa. Quindi, costruisco qualcosa del genere:

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 );
        }
    }
}

In questo modo:

  • Una classe che desidera fornire un'implementazione di Zork può essere dichiarata con implements Zork.Defaults e astenersi dall'implementare metodi per i quali sono stati forniti valori predefiniti.

  • Una classe che desidera agire come decoratore di Zork può essere dichiarata con implements Zork.Decorator , fornire un'implementazione per getDecoratedZork() e implementare qualsiasi sottoinsieme di Zork è necessario, consentendo il resto dei metodi da delegare al Zork decorato.

  • Una classe che desidera moltiplicare l'eredità di Zork può essere dichiarata come segue:

(Da quando Zork è un'interfaccia, la classe MultipleInheritor può implementare molte altre interfacce come 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;
    }
}
    
risposta data 19.09.2015 - 22:13
fonte

Leggi altre domande sui tag