Ho una classe di base, Base
. Ha due sottoclassi Sub1
e Sub2
. Ogni sottoclasse ha alcuni metodi aggiuntivi. Ad esempio, Sub1
ha Sandwich makeASandwich(Ingredients... ingredients)
e Sub2
ha boolean contactAliens(Frequency onFrequency)
.
Poiché questi metodi adottano parametri diversi e fanno cose completamente diverse, sono completamente incompatibili e non posso semplicemente usare il polimorfismo per risolvere questo problema.
Base
fornisce la maggior parte delle funzionalità e ho una vasta collezione di Base
oggetti. Tuttavia, tutti gli Base
oggetti sono o a Sub1
o a Sub2
volte e a volte ho bisogno di sapere quali sono.
Sembra una cattiva idea fare quanto segue:
for (Base base : bases) {
if (base instanceof Sub1) {
((Sub1) base).makeASandwich(getRandomIngredients());
// ... etc.
} else { // must be Sub2
((Sub2) base).contactAliens(getFrequency());
// ... etc.
}
}
Quindi ho escogitato una strategia per evitarlo senza lanciare. Base
ora ha questi metodi:
boolean isSub1();
Sub1 asSub1();
Sub2 asSub2();
E, naturalmente, Sub1
implementa questi metodi come
boolean isSub1() { return true; }
Sub1 asSub1(); { return this; }
Sub2 asSub2(); { throw new IllegalStateException(); }
E Sub2
li implementa in modo opposto.
Purtroppo, ora Sub1
e Sub2
hanno questi metodi nella loro API. Quindi posso farlo, per esempio, su Sub1
.
/** no need to use this if object is known to be Sub1 */
@Deprecated
boolean isSub1() { return true; }
/** no need to use this if object is known to be Sub1 */
@Deprecated
Sub1 asSub1(); { return this; }
/** no need to use this if object is known to be Sub1 */
@Deprecated
Sub2 asSub2(); { throw new IllegalStateException(); }
In questo modo, se si sa che l'oggetto è solo a Base
, questi metodi non sono deprecati e possono essere usati per "lanciarsi" su un tipo diverso in modo da poter invocare i metodi della sottoclasse su di esso. Questo mi sembra elegante in un certo senso, ma d'altra parte sto abusando delle annotazioni obsolete come un modo per "rimuovere" i metodi da una classe.
Dal momento che Sub1
un'istanza è davvero una Base, ha senso usare l'ereditarietà piuttosto che l'incapsulamento. Quello che sto facendo è buono? C'è un modo migliore per risolvere questo problema?
instanceof
, in un modo che richiede molta digitazione, è soggetto a errori e rende difficile aggiungere più sottoclassi.
Sub1
e Sub2
non possono essere usati in modo intercambiabile, allora perché li trattate come tali? Perché non tenere traccia dei tuoi "produttori di sandwich" e dei "personaggi alieni" separatamente?