Domanda

E 'meglio mettere un implementazione predefinita di un metodo in una superclasse, e override quando sottoclassi vogliono deviare da questo, o si dovrebbe lasciare il metodo della superclasse astratta, e hanno l'attuazione normale ripetuto in molte sottoclassi?

Ad esempio, un progetto che sono coinvolto in ha una classe che viene utilizzato per specificare le condizioni in cui si deve fermare. La classe astratta è la seguente:

public abstract class HaltingCondition{
    public abstract boolean isFinished(State s);
}

Un'implementazione banale potrebbe essere:

public class AlwaysHaltingCondition extends HaltingCondition{
    public boolean isFinished(State s){
        return true;
    }
}

Il motivo per cui lo facciamo con gli oggetti è che possiamo allora comporre arbitrariamente questi oggetti insieme. Per esempio:

public class ConjunctionHaltingCondition extends HaltingCondition{
    private Set<HaltingCondition> conditions;

    public void isFinished(State s){
        boolean finished = true;
        Iterator<HaltingCondition> it = conditions.iterator();
        while(it.hasNext()){
            finished = finished && it.next().isFinished(s);
        }
        return finished;
    }
}

Comunque, abbiamo alcune condizioni di sosta che devono essere notificato che si sono verificati gli eventi. Per esempio:

public class HaltAfterAnyEventHaltingCondition extends HaltingCondition{
    private boolean eventHasOccurred = false;

    public void eventHasOccurred(Event e){
        eventHasOccurred = true;
    }

    public boolean isFinished(State s){
        return eventHasOccurred;
    }
}

Come dovremmo meglio rappresentare eventHasOccurred(Event e) nella superclasse astratta? La maggior parte delle sottoclassi possono avere un no-op implementazione di questo metodo (ad es AlwaysHaltingCondition), mentre alcuni richiedono un significativo implementazione di funzionare correttamente (ad es HaltAfterAnyEventHaltingCondition) e altri non hanno bisogno di fare qualcosa con il messaggio, ma deve trasmetterla ai loro subordinati in modo che essi possano operare correttamente (es ConjunctionHaltingCondition).

potrebbe avere un'implementazione di default, che ridurrebbe la duplicazione del codice, ma causerebbe alcune sottoclassi di compilare ancora non funziona correttamente se non è stato sovrascritto, o potrebbe avere il metodo dichiarato come astratto, che richiederebbe l'autore di ogni sottoclasse di pensare l'attuazione che fornivano, anche se nove volte su dieci sarebbe un'implementazione no-op. Quali sono gli altri pro ei contro di queste strategie? È uno molto migliore rispetto agli altri?

È stato utile?

Soluzione

Una possibilità è quella di avere un'altra sottoclasse astratta, da utilizzare come superclasse per tutte le implementazioni che do desidera utilizzare l'implementazione predefinita.

Personalmente di solito lasciare metodi non-finali astratti in una classe astratta (o usare le interfacce solo invece) ma dipende sicuramente dalla situazione. Se si dispone di un'interfaccia con molti metodi, e si vuole essere in grado di solo opt-in per alcuni di loro, per esempio, allora una classe astratta che implementa l'interfaccia in un modo no-op per ogni metodo va bene.

È necessario valutare caso per caso nel merito, in fondo.

Altri suggerimenti

Se avete intenzione di mettere qualsiasi implementazione nella classe base astratta, dovrebbe essere il codice per le sottoclassi che utilizzano l'applicazione no-op, poiché questa è un'implementazione che ha senso per la classe di base pure. Se non ci fosse l'attuazione ragionevole per la classe di base (ad esempio, se non ci fosse sensato no-op per il metodo che si sta discutendo qui), quindi suggerirei lasciando astratta.

Per quanto riguarda il codice duplicato, se ci sono "famiglie" di classi che utilizzano la stessa applicazione del metodo e non si desidera duplicare il codice in tutte le classi in famiglia, si potrebbe usare semplicemente helper classi per famiglia che forniscono queste implementazioni. Nel tuo esempio, un aiuto per le classi che passano verso il basso gli eventi, un aiutante per le classi accettare e registrare l'evento, ecc.

Sembra che si sono preoccupati per l'impostazione che variabile booleana quando l'evento accade. Se le sostituzioni utente eventHasOccurred(), quindi la variabile booleana non sarà impostata e isFinished() non restituirà il valore corretto. Per fare questo, si può avere un metodo astratto che le sostituzioni degli utenti di gestire l'evento e un altro metodo che chiama il metodo astratto e imposta il valore booleano (vedere il codice di esempio).

Inoltre, invece di mettere il metodo eventHasOccurred() nella classe HaltingCondition, si può solo avere le classi che hanno bisogno di eventi manico estendere una classe che definisce questo metodo (come la classe più avanti). Ogni classe che non ha bisogno di eventi maniglia può solo estendere HaltingCondition:

public abstract class EventHaltingCondition extends HaltingCondition{
  private boolean eventHasOccurred = false;

  //child class implements this
  //notice how it has protected access to ensure that the public eventHasOccurred() method is called
  protected abstract void handleEvent(Event e);

  //program calls this when the event happens
  public final void eventHasOccurred(Event e){
    eventHasOccurred = true; //boolean is set so that isFinished() returns the proper value
    handleEvent(e); //child class' custom code is executed
  }

  @Override
  public boolean isFinished(){
    return eventHasOcccurred;
  }
}

EDIT (vedi commenti):

final EventHaltingCondition condition = new EventHaltingCondition(){
  @Override
  protected void handleEvent(Event e){
    //...
  }
};
JButton button = new JButton("click me");
button.addActionListener(new ActionListener(){
  public void actionPerformed(ActionEvent actionEvent){
    //runs when the button is clicked

    Event event = //...
    condition.eventHasOccurred(event);
  }
});

Ho incontrato uno scenario simile, quando ho creato la struttura di base (gerarchia di classi) di un'applicazione stavo sviluppando insieme ad altri al lavoro. Mia scelta per l'immissione di un metodo abstract (e conseguentemente di forzare la sua attuazione) sia per comunicazione scopi.

In sostanza gli altri compagni di squadra hanno avuto in qualche modo per implementare in modo esplicito il metodo e quindi prima di tutto notare la sua presenza e la seconda accordo su quello che ci restituiscono, anche se è solo l'implementazione di default.

implementazioni predefinite in classi di base spesso avere trascurato.

Se si lascia il metodo di super classe astratta si potrebbe voler guardare in utilizzando un'interfaccia (da non confondere con un'interfaccia). Poiché l'interfaccia è uno che non fornisce una concreta attuazione.

Scott

Per espandere, quando noi, come i programmatori sono istruiti a codice per un'interfaccia spesso i tempi nuovi, e talvolta con esperienza, gli sviluppatori assumeremo in modo non corretto che è in riferimento alla parola di interfaccia in cui nessun dettaglio di attuazione possono essere trovati. Tuttavia, il modo più chiaro di dire questo è che qualsiasi oggetto di livello superiore può essere trattato come un'interfaccia che può essere interagito con. Per esempio una classe astratta denominata animali sarebbe un'interfaccia sarebbe una classe chiamata gatto che avrebbe ereditato da animali.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top