Se ti capisco correttamente, sembra che tu stia lavorando con qualcosa come questo pseudocodice:
void foo(Item i) {
if(i instanceof Clock) {
(i as Clock).increment();
} else if(i instanceof Chair) {
(i as Chair).sit();
}
}
Come sospetti, questo è un odore di codice. In realtà piuttosto strong.
Il problema immediato è che, poiché questa funzione deve conoscere il tipo concreto di i
, non c'è assolutamente alcun vantaggio nell'usare la classe base Item
. In effetti, questa funzione sta affermando che può prendere qualsiasi oggetto Item
arbitrario, quando non può, quindi sta mentendo a tutti coloro che cercano di usarlo. Se sei molto fortunato, la soluzione è semplice come dividerla in void foo(Clock c)
e void foo(Chair c)
, ma potrebbe essere più profonda di quella.
Normalmente, questo accade quando si progetta la gerarchia degli oggetti attorno alle cose del mondo reale che si desidera "esistere" nel programma, piuttosto che attorno alle azioni che si desidera effettivamente eseguire il programma . L'unica soluzione semplice è ridisegnare la parte rilevante del tuo programma. Sfortunatamente, senza sapere nulla del resto del tuo programma, possiamo solo indovinare quale sarebbe il cambiamento ottimale. Da un lato, forse puoi trovare una singola interfaccia Interactable
che soddisfi le esigenze di tutti i tuoi oggetti e dei loro chiamanti. D'altra parte, forse non ha alcun senso provare a unificare tutti questi tipi disparati e dovresti spostare il codice Chair
lontano dal codice Clock
. E ci sono molte altre opzioni in mezzo. In ogni caso, sarà difficile capire quale sia il design corretto, ma risolvere il problema ora ti farà risparmiare molto tempo (a meno che questo progetto non rimanga molto piccolo e semplice) .
Nella speranza di essere leggermente più utili di "questo è un problema difficile, ma buona fortuna", proverò a delineare una semplice soluzione di esempio. Il tuo esempio mi ricorda i videogiochi che hanno un pulsante "interagisci", quindi diciamo che sto progettando il codice di interazione dell'articolo per un gioco del genere. Potrei iniziare chiedendomi quali chiavi, munizioni, kit di pronto soccorso, talismani e sacchetti di plastica hanno in comune, ma poi finisco nella situazione che hai descritto.
Invece vorrei chiedere cosa succede quando interagisco con ognuno di questi elementi. Quello che trovo è che, almeno per il gioco specifico che sto giocando oggi , alcune delle possibili risposte includono:
- Voglio rimuovere l'elemento dallo schermo, aggiungerlo all'inventario, mostrare un modello 3d di esso insieme a un messaggio "I got X", quindi attendere la pressione di un pulsante.
- Voglio mettere in pausa il gioco, mostrare del testo nella parte inferiore dello schermo, attendere che l'utente lo scorra premendo i pulsanti, quindi riprendi.
- Come il # 2, ad eccezione dell'ultima porzione di testo che include una scelta, e in base alla selezione dell'utente, possiamo mostrare un'animazione, cambiare lo stato di alcuni altri oggetti nella stanza e generare alcuni mostri prima di riprendere.
Sarebbe molto sensato scrivere un'interfaccia standard per ognuno di questi tipi di elementi, dal momento che tutti gli elementi di un determinato tipo fanno essenzialmente le stesse cose; puoi scrivere una funzione che prende un ShowTextInteractable
a cui non importa se è un Book
o un CreepyDoll
o un Flowerpot
(e potrebbe anche non usare la sottoclasse, forse new ShowTextInteractable("It's a flowerpot.")
è abbastanza buono). Notare anche che non ho nemmeno toccato l'uso di oggetti dall'inventario; quelli dovrebbero essere tipi completamente diversi dagli oggetti "nel mondo" perché fanno cose completamente diverse. Ma spero che ti dia un'idea di cosa sto parlando.