OOD: ereditarietà di Java e accesso ai metodi figlio tramite casting

8

Ho diverse classi Parent e Child1 ... Child9 implementate in Java. Parent è una classe astratta, contenente tutte le variabili comuni delle classi figlie (molto, che è il motivo principale per cui ho creato Parent una classe astratta e non un'interfaccia), alcuni metodi astratti e alcuni implementati.

Alcune delle classi figlie hanno metodi personalizzati che sono specifici per loro. Così spesso mi trovo a chiamare un metodo figlio usando downcasting:

Parent p = new Child1();
((Child1) p).child1SpecificMethod();

In qualche modo ho la sensazione che questa sia una cattiva pratica OOD, ma non sono del tutto sicuro che sia questo il vero rispettivamente il modo in cui migliorare il design.

- Modifica - Quello che probabilmente dovrei cambiare comunque è il fatto che io uso la classe Parent per organizzare molte (per ora) variabili comuni, rendendole membri (o un oggetto contenitore) delle classi concrete.

    
posta jpmath 10.10.2014 - 10:44
fonte

4 risposte

11

Questo non è solo cattiva pratica , questo è inutile complicato.

Perché usi ereditarietà in generale?

Quando usi l'ereditarietà, hai un insieme di comportamenti comuni, che vuoi rendere disponibili per molti vettori diversi. Ciò include ereditarietà della classe e anche l'ereditarietà delle interfacce . heir , per così dire, è spesso una specializzazione della classe da cui eredita; che è principalmente vero per l'ereditarietà di classe.

Pensa a una classe auto e a una sottoclasse porsche (la tipica è una ) correlazione). Hai un comportamento generale come avvio / arresto del motore , guida e così via. Se tratti una porsche come un'auto, sei legato a questo aspetto del suo comportamento. Se sai, che vuoi solo un porsche e gestiscilo solo come porsche , è ridondante instivare un porsche come un < em> car e ottieni porsche-behavior via casting .

Il polimorfismo ha senso al contrario:

Hai una porsche e hai bisogno di trattarla dall'aspetto di un'auto; per esempio. guida

Finché il tuo porsche accetta virare a sinistra , sterzare a destra , spostare verso l'alto , shift in basso , ecc. potresti usare il polimorfismo / sostituzione uno per l'altro.

È meglio, per istanziare i tuoi oggetti nella loro forma specializzata. Quindi puoi sfruttare al massimo il polimorfismo e usarlo solo quando hai bisogno di .

Detto questo: Parent p = new Child1(); non ha senso per me.

Modifica: implementerei porsche differente (tramite composizione ), ma per il gusto dell'esempio, è una macchina.

    
risposta data 10.10.2014 - 16:34
fonte
4

Il tuo sentimento è giusto per considerarlo una cattiva pratica. Immagina il tuo codice di esempio un po 'diverso:

Parent p = createObject();
((Child1) p).child1SpecificMethod();

Come fai a sapere, che il valore di p è davvero Child1? Non lo fai e questo può causare una ClassCastException in fase di esecuzione. Se è necessario chiamare child1SpecificMethod () nel codice, è necessario assicurarsi che p sia di tipo Child1. Se ciò non è possibile perché l'oggetto p viene passato al tuo codice (ad esempio come parametro metodo) come tipo Parent, potresti prendere in considerazione l'utilizzo di una variante di Visitor-Pattern ed esegui child1SpecificMethod nell'handle-Method dell'oggetto visitatore, che gestisce Child1.

    
risposta data 10.10.2014 - 13:49
fonte
3

Utilizza invece la ricerca di funzionalità Non dare accesso alle classi figlie, considerale come implementazioni della classe Genitore.

Quindi definisci le interfacce specificando alcune capacità , funzionalità.

interface Child1Specific {
    void child1SpecificMethod();
}

Utilizzo:

Parent parent = ...
Child1Specific specific = parent.lookup(Child1Specific.class);
if (specific1 != null) {
    specific1.child1SpecificMethod();
}

Questo meccanismo di scoperta è molto flessibile. L'utilizzo della delega invece dell'ereditarietà può essere piuttosto gratificante. Nota, avere classi figlio non è più necessario.

O in java 8 (dove sono possibili diverse varianti e anche l'interfaccia potrebbe essere funzionale):

Optional<Child1Specific> specific = parent.lookup(Child1Specific.class);
if (specific1.isPresent()) {
    specific1.get().child1SpecificMethod();
}

Crea nella classe Genitore una ricerca per alcune funzionalità:

public class Parent {
    protected final Map<Class<?>, Object> capabilities = new HashMap<>();
    protected final <T> void registerCapability(Class<T> klass, T object);

    public <T> T lookup(Class<T> klass) {
        Object object = capabilities.get(klass);
        return object == null ? null : klass.cast(object);
    }

O in java 8:

    public <T> Optional<T> lookup(Class<T> klass) {
        Object object = capabilities.get(klass);
        return Optional.ofNullable(klass.cast(object));
    }

La classe figlio:

class Child1 extend Parent implements Child1Specific {
    Child1() {
        registerCapability(Child1Specific.class, this);
    }
}

O più dinamico:

class Child1 extends Parent {
    private Child1Specific specific = new Child1Specific() {
        ... Parent.this ...
    };
    Child1() {
        registerCapability(Child1Specific.class, specific);
    }
}
    
risposta data 10.10.2014 - 15:16
fonte
-3

Aggiungi un metodo astratto child1SpecificMethod () nella classe Parent (devi contrassegnare la classe come abstract) e fornirne l'implementazione nella rispettiva classe child.

    
risposta data 08.10.2015 - 12:22
fonte

Leggi altre domande sui tag