2015-06-26 23 views
11

Sto leggendo J. Bloch Java efficace e ora sono in sezione eredità vs composizione. Per quanto ho capito ha detto che l'eredità non è sempre buona.Ereditarietà Java vs inizializzazione

Una causa correlata di fragilità nelle sottoclassi è che la loro superclasse può acquisire nuovi metodi nelle versioni successive. Supponiamo che un programma dipenda dalla sua sicurezza sul fatto che tutti gli elementi inseriti nella serie soddisfano alcuni predicati. Questo può essere garantito da sottoclasse della raccolta e sovrascrivendo ogni metodo capace di aggiungendo un elemento di assicurare che il predicato è soddisfatto prima aggiungendo l'elemento. Funziona bene fino a un nuovo metodo in grado di inserendo un elemento viene aggiunto alla superclasse in una successiva versione .

Ma perché non funziona? La superclasse è solo l'interfaccia Collection e se aggiungiamo un nuovo metodo abbiamo solo un errore in fase di compilazione. Questo non è dannoso in assoluto ...

+1

L'aggiunta di un nuovo metodo a una super * classe * non causa un errore simile in fase di compilazione. Infatti, l'aggiunta di metodi 'default' a un'interfaccia non produce neanche un errore di compilazione. –

+0

Se stai parlando di 'java.util.Collection', NON è una super classe. Questa è solo un'interfaccia. – Alderath

+0

L'aggiunta di un metodo astratto a super classe o interfaccia è un errore in fase di compilazione se la sottoclasse viene ricompilata, ma ciò potrebbe non avvenire. –

risposta

19

Supponiamo di avere una superclasse Collection in qualche biblioteca v1.0:

public class MyCollection { 
    public void add(String s) { 
     // add to inner array 
    } 
} 

È sottoclasse che per accettare solo le stringhe che hanno lunghezza 5:

public class LimitedLengthCollection extends MyCollection { 
    @Override 
    public void add(String s) { 
     if (s.length() == 5) { 
      super.add(s); 
     } 
    } 
} 

Il contratto, l'invariante di questa classe è che non conterrà mai una stringa che non ha lunghezza 5.

Ora viene rilasciata la versione 2.0 della libreria e si inizia a utilizzarla. La classe base è modificata in:

public class MyCollection { 
    public void add(String s) { 
     // add to inner array 
    } 

    public void addMany(String[] s) { 
     // iterate on each element and add it to inner array 
    } 
} 

e la sottoclasse non viene modificata.Ora gli utenti della sottoclasse possono fare

LimitedLengthCollection c = new LimitedLengthCollection(); 
c.addMany(new String[] {"a", "b", "c"}); 

e quindi il contratto della sottoclasse viene interrotto. Si supponeva che accettasse solo stringhe di lunghezza 5 e non più, perché è stato aggiunto un ulteriore metodo nella superclasse.

+0

Sebbene questo sia il nocciolo del problema (fragile classe base), si verifica solo se il metodo 'addMany' non usa il metodo' add' ma implementa il comportamento di aggiunta stesso. Forse dovresti aggiungere alcuni dettagli di implementazione a tutti quei metodi. – Seelenvirtuose

+2

corretto. E ciò causerebbe un altro problema. La versione 2.0 potrebbe delegare ad add(), e potreste fare affidamento su di essa per effettuare il controllo solo in add(), mentre la versione 3.0 non delegherebbe più da aggiungere(), e il vostro controllo verrebbe quindi bypassato. –

+0

Quindi, il punto qui è 'MyCollection' è una classe concreta. Fatto. Ma in Java 8, l'implementazione delle interfacce della libreria è ancora sicura? Possono fornire un'implementazione di default per alcuni metodi e se ne implementiamo uno alla versione 1.0 e alla versione 2.0 qualche metodo viene aggiunto con l'implementazione di default, probabilmente ci metteremo anche nei guai (come hai fatto notare all'invariant non funzionante). –

2

Perché (in generale) interromperà il codice client che ha implementato la classe Collection.

In questo particolare esempio, la sicurezza verrà interrotta perché gli utenti malintenzionati sarebbero in grado di inserire elementi utilizzando il metodo non ancora sovrascritto che è stato aggiunto dopo aver spedito il codice.

Basare il codice su classi ereditate che non controlli può morderti in futuro.

+0

Sarebbe una buona cosa in questo caso; se il codice del client si rompe (non riesce a compilare), allora sarà necessario ripararlo prima che possa essere eseguito, il che significa che la minaccia alla sicurezza viene evitata. –

+0

Potete immaginare che Oracle altera la classe/interfaccia Collection in un secondo punto? Quale sarebbe il problema con questo? – idipous

+0

Non sono in disaccordo sul fatto che alterare un'interfaccia pubblica del genere sarebbe una cosa negativa; Sto semplicemente sottolineando che la domanda riguarda i problemi di sicurezza, nel qual caso l'interruzione del codice sarebbe preferibile a causare un problema di sicurezza. –

2

se aggiungiamo un nuovo mehtod abbiamo solo un errore di compilazione

Questo è vero solo quando si aggiunge un abstract metodo alla superclasse/interfaccia. Se viene aggiunto un metodo non astratto, è perfettamente valido non sovrascrivere quel nuovo metodo.

3

Il problema non è che l'ereditarietà non potrebbe funzionare.

Il problema è che con l'ereditarietà lo sviluppatore non può imporre un comportamento (come l'esempio della collezione che soddisfa alcuni predicati).

Quando creiamo una nuova classe raramente è davvero un tipo specializzato di un'altra. Più spesso è qualcosa di nuovo che usa altre classi.

Così raramente abbiamo bisogno di ereditarietà e più spesso abbiamo bisogno di creare una classe che utilizzi altre classi per fare qualcosa.

Il È UN vs HA UN

Bisogna chiedersi:

Classe B è un nuovo tipo sub di classe A che fare le stesse cose di A in modo diverso?

o

Classe B ha una classe all'interno di fare qualcosa di diverso da quello che A è intented fare?

E sappiate che più spesso la risposta giusta è quest'ultima.