Ho una classe di base, Base. Ha due sottoclassi Sub1e Sub2. Ogni sottoclasse ha alcuni metodi aggiuntivi. Ad esempio, Sub1ha Sandwich makeASandwich(Ingredients... ingredients)e Sub2ha 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.
Basefornisce la maggior parte delle funzionalità e ho una vasta collezione di Baseoggetti. Tuttavia, tutti gli Baseoggetti sono o a Sub1o a Sub2volte 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. Baseora ha questi metodi:
boolean isSub1();
Sub1 asSub1();
Sub2 asSub2();
E, naturalmente, Sub1implementa questi metodi come
boolean isSub1() { return true; }
Sub1 asSub1(); { return this; }
Sub2 asSub2(); { throw new IllegalStateException(); }
E Sub2li implementa in modo opposto.
Purtroppo, ora Sub1e Sub2hanno 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 Sub1un'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.
Sub1e Sub2non 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?