Polimorfismo
Finché usi getType()
o qualcosa del genere, non stai usando il polimorfismo.
Capisco che hai bisogno di sapere che tipo hai. Ma qualsiasi lavoro che vorresti fare sapendo che dovrebbe davvero essere inserito nella classe. Allora gli dici solo quando farlo.
Il codice procedurale ottiene informazioni e prende decisioni. Il codice orientato agli oggetti dice agli oggetti di fare le cose.
- Alec Sharp
Questo principio si chiama dire, non chiedere . In seguito ti aiuta a non diffondere dettagli come digitare e creare la logica che agisce su di essi. In questo modo si capovolge una lezione. È meglio mantenere quel comportamento all'interno della classe in modo che possa cambiare quando cambia la classe.
incapsulamento
Puoi dirmi che non saranno mai necessarie altre forme, ma io non ti credo e nemmeno tu dovresti.
Un buon effetto del seguente incapsulamento è che è facile aggiungere nuovi tipi perché i loro dettagli non si diffondono nel codice in cui si presentano if
e nella switch
logica. Il codice per un nuovo tipo dovrebbe essere tutto in un posto.
Un tipo di sistema di rilevamento delle collisioni ignorante
Lascia che ti mostri come progetterei un sistema di rilevamento delle collisioni che è performante e funziona con qualsiasi forma 2D senza preoccuparsi del tipo.
Di 'che avresti dovuto disegnarlo. Sembra semplice Sono tutti cerchi. È allettante creare una classe circolare che capisca le collisioni. Il problema è che questo ci manda giù una linea di pensiero che cade a pezzi quando abbiamo bisogno di 1000 cerchi.
Non dovremmo pensare ai circoli. Dovremmo pensare ai pixel.
E se ti dicessi che lo stesso codice che usi per disegnare questi ragazzi è quello che puoi usare per rilevare quando si toccano o anche su quali l'utente sta facendo clic.
Qui ho disegnato ogni cerchio con un colore unico (se i tuoi occhi sono abbastanza buoni da vedere il contorno nero, ignoralo). Ciò significa che ogni pixel in questa immagine nascosta si ricollega a ciò che l'ha disegnato. Una hashmap si occupa di questo in modo piacevole. Puoi effettivamente fare il polimorfismo in questo modo.
Questa immagine non devi mai mostrarla all'utente. Lo crei con lo stesso codice che ha disegnato il primo. Solo con colori diversi.
Quando l'utente fa clic su un cerchio, so esattamente quale cerchio perché solo un cerchio è quel colore.
Quando disegno un cerchio sopra un altro posso leggere rapidamente ogni pixel che sto per sovrascrivere scaricandoli in un set. Quando ho finito i set point per ogni cerchia con cui si è scontrato e ora devo chiamare ciascuno solo una volta per avvisarlo della collisione.
Un nuovo tipo: rettangoli
Tutto questo è stato fatto con i cerchi ma ti chiedo: funzionerebbe in modo diverso con i rettangoli?
Nessuna conoscenza del cerchio è trapelata nel sistema di rilevamento. Non importa del raggio, della circonferenza o del punto centrale. Si preoccupa per i pixel e il colore.
L'unica parte di questo sistema di collisione che deve essere inserito nelle singole forme è un colore unico. Diverso da quello che le forme possono solo pensare a disegnare le loro forme. È quello che sono bravi in ogni caso.
Ora quando scrivi la logica di collisione non ti interessa quale sottotipo hai. Gli dici di scontrarsi e ti dice cosa ha trovato sotto la forma che finge di disegnare. Non c'è bisogno di conoscere il tipo. Ciò significa che puoi aggiungere tutti i sottotipi che desideri senza dover aggiornare il codice in altre classi.
Scelte di implementazione
Davvero, non deve essere un colore unico. Potrebbe essere un vero riferimento a un oggetto e salvare un livello di riferimento indiretto. Ma quelli non sarebbero così belli se disegnati in questa risposta.
Questo è solo un esempio di implementazione. Ce ne sono sicuramente altri. Ciò che questo doveva mostrare è che più si lasciano attaccare questi sottotipi di forma con la loro unica responsabilità, meglio funziona l'intero sistema. Probabilmente ci sono soluzioni più veloci e meno dispendiose in termini di memoria, ma se mi costringono a diffondere la conoscenza dei sottotipi intorno, sarei detestabile usarli anche con i miglioramenti delle prestazioni. Non li userei se non ne avessi chiaramente bisogno.
Doppia spedizione
Fino ad ora ho completamente ignorato la doppia spedizione . L'ho fatto perché potevo. Fintanto che alla logica di collisione non importa quali due tipi si sono scontrati, non ne hai bisogno. Se non ti serve, non usarlo. Se pensi di averne bisogno, rimandaci il più a lungo possibile. Questo atteggiamento si chiama YAGNI .
Se decidi di aver davvero bisogno di diversi tipi di collisioni, chiedi a te stesso se n sottotipi di forma hanno davvero bisogno di n 2 tipi di collisioni. Finora ho lavorato molto duramente per rendere semplice l'aggiunta di un altro sottotipo di forma. Non voglio rovinarlo con un'implementazione a doppia spedizione che costringe i circoli a sapere che esistono dei quadrati.
Quanti tipi di collisioni ci sono comunque? Un po 'di speculazione (una cosa pericolosa) inventa collisioni elastiche (gonfiabili), anelastiche (appiccicose), energiche (esplosive) e distruttive (dannose). Potrebbero essercene di più, ma se questo è inferiore a n 2, non progettiamo eccessivamente le nostre collisioni.
Ciò significa che quando il mio siluro colpisce qualcosa che accetta danni non deve sapere che colpisce una nave spaziale. Deve solo dirgli "Ah ah! Hai subito 5 punti di danno".
Le cose che infliggono danni inviano messaggi di danno a cose che accettano messaggi di danno. Fatto ciò, puoi aggiungere nuove forme senza dire alle altre forme della nuova forma. Finisci solo per diffonderti attorno a nuovi tipi di collisioni.
La nave spaziale può rimandare al torp "Ah ah! Hai subito 100 punti di danno". così come "Ora sei bloccato sul mio scafo". E il torp può rispedire "Beh, ho finito per dimenticarmi di me".
In nessun momento sa esattamente cosa sia ciascuno. Sanno solo come parlarsi attraverso un'interfaccia di collisione.
Ora, il doppio invio ti consente di controllare le cose più intimamente di così, ma lo vuoi davvero ?
Se lo fai, ti preghiamo almeno di pensare a fare una doppia spedizione attraverso le astrazioni di quali tipi di collisioni accetta una forma e non sull'attuazione effettiva della forma. Inoltre, il comportamento alla collisione è qualcosa che puoi iniettare come dipendenza e delegare a quella dipendenza.
Prestazione
Le prestazioni sono sempre fondamentali. Ma ciò non significa che sia sempre un problema. Test delle prestazioni. Non limitarti a speculare. Sacrificare tutto il resto in nome della performance di solito non porta comunque al codice di perforazione.