Sto leggendo il libro "Effective Java" che suggerisce di favorire la composizione sull'ereditarietà. L'esempio che mostra mostra qualcosa di simile a questo:
public class InstrumentedHashSet<E> extends HashSet<E> {
// The number of attempted element insertions
private int addCount = 0;
public InstrumentedHashSet() { }
public InstrumentedHashSet(int initCap, float loadFactor) {
super(initCap, loadFactor);
}
@Override public boolean add(E e) {
addCount++;
return super.add(e);
}
@Override public boolean addAll(Collection<? extends E> c) {
addCount += c.size();
return super.addAll(c);
}
public int getAddCount() {
return addCount;
}
}
Questo presenta un problema perché l'implementazione di addAll
in HashSet
utilizza il metodo add
. Pertanto, quando si chiama addAll
ogni nuovo elemento viene conteggiato due volte: una volta in addAll
e una volta in add
.
Nel prossimo capitolo viene spiegato che se scegliamo di consentire l'estensione della nostra classe, dovremmo esplicitamente spiegare il funzionamento interno dei nostri metodi che usano metodi sovrascrivibili (nel senso: nella documentazione addAll
dovremmo specificare il suo uso di add
e commit a questa implementazione per sempre ).
Penso che una migliore pratica sarebbe quella di decidere che ogni metodo che è sia un metodo API overridable, sia usato dall'implementazione interna dovrebbe essere estratto in un metodo interno, privato. Quindi il nostro ideale HashSet
avrebbe questi metodi:
public class HashSet<E> implements Set<E> {
// Ignore irrelevant code
@Override public boolean add(E e) {
// The new add implementation only wraps the innerAdd method
return innerAdd(e);
}
@Override public boolean addAll(Collection<? extends E> c) {
// Do the same logic as before but use innerAdd instead of add
}
private boolean innerAdd(E e) {
// The original add logic will be moved to here
}
}
In questo modo incapsuliamo l'implementazione interna e permettiamo di estendere la classe senza temere di sovrascrivere un metodo che viene utilizzato dall'implementazione interna. Ovviamente, possiamo usare l'aggiunta originale come prima, perché chiamando super.add(e)
di fatto dobbiamo usare innerAdd
, quindi non è un problema.
Vorrei sapere se questa potrebbe essere una buona pratica (non per HashSet<E>
che è già impegnata per il codice che la usa, ma per le nuove classi che devono essere sostituite)?