Consentitemi di fornire una risposta diversa, ma tipica: dipende.
Molte persone (me incluse) hanno problemi con gerarchie di ereditarietà come quelle di @greyfade menzionate nell'altra risposta. Il mantra del codice pulito "preferisce la composizione sull'ereditarietà" deriva esattamente da questo.
Tuttavia , comprendendo i problemi che sono alla base delle gerarchie di ereditarietà, puoi ancora ottenere il meglio da entrambi i mondi. Tenete presente, tuttavia, che questo dipende in gran parte dalla vostra lingua e dalla vostra autodisciplina.
Quello che hai descritto è qualcosa che semplicemente non era possibile in un linguaggio come Java (precedente a Java 8, che ora ha le interfacce predefinite), ma accettato prontamente in C ++ prima e in linguaggi più moderni come C # o Scala. Per salvare le noiose discussioni originate dai problemi sopra menzionati, tuttavia, hanno scelto di chiamare questi "mixin" o "tratti" (in opposizione a "multiple" "eredità" - due parole che sono prontamente attaccabili da tutti con un livello CS di base). In sostanza, la tua descrizione sembra proprio come la stai facendo.
Ciò che è importante in un approccio basato sui tratti, proprio come in uno basato sulla composizione (anziché sull'eredità), è quello di concentrarsi su due vantaggi principali:
-
Vuoi essere in grado di riutilizzare una di queste "cose" senza doversi preoccupare degli altri
-
e vuoi poter cambiare o sostituire uno di questi, sempre senza preoccuparti troppo degli altri.
Chiaramente, quando si ha una gerarchia completa di cose in un grande albero, è quasi impossibile ottenere questi due vantaggi per una classe di implementazione che si trova da qualche parte nel mezzo di quell'albero. Tuttavia, quando lavori con tratti / mixin, o essenzialmente fai la stessa cosa attraverso l'autodisciplina in C ++ in quanto erediti più volte, ma non forma una gerarchia che ha una profondità maggiore di un singolo passo di ereditarietà, allora hai tecnicamente usato l'ereditarietà, eppure puoi mantenere tutti questi vantaggi.
Puoi riutilizzare ciascuna delle singole parti in modo indipendente, o persino scambiarle con un lavoro ragionevolmente piccolo. Eppure questo sistema non ti salva di creare potenzialmente gerarchie di classi intangibili di nuovo (ma poi di nuovo, neanche un martello ti risparmia di colpire la testa con esso).
di responsabilità;
Per quanto riguarda i sistemi di entità, personalmente non sono convinto della loro superiorità rispetto ai tratti. Entrambi sono molto meglio di una grande gerarchia di ereditarietà. Inoltre non sono nel settore dei giochi, quindi altri effetti (come i citati accessi veloci alla memoria) possono anche essere abbastanza importanti per il tuo caso d'uso per giustificare la scelta di un modo rispetto all'altro. Come al solito, non c'è un proiettile d'argento (a meno che non ne crei uno nel gioco) e tutto ha i suoi pro e contro. Assicurati solo di comprendere gli aspetti negativi di qualsiasi scelta.
In risposta alle domande del commento aggiuntivo:
Non è che i diversi approcci differiscano dall'essere adatti o meno per un semplice gioco. Non credo che tu abbia problemi di scalabilità con nessuno dei due. I tratti / Mixin sono usati con successo in sistemi piuttosto grandi e si adattano bene, specialmente perché sono così piacevolmente componibili. Non ho idea di come sia venuta la scalabilità, ma non è davvero un problema per nessuno dei due approcci, per quanto posso dire.
Affrontare il tuo commento, per quanto ho capito, basato sui dati non significa che tu abbia una composizione standard tramite un attributo nella tua classe. Pertanto la tua classe Entity
non fa riferimento direttamente a una classe Position
, poiché ciò comporterebbe di nuovo un accoppiamento più elevato del previsto. Per aggiungere un "componente" a un'entità, dovresti comunque andare in quella classe. Per quanto ne so, e per favore sii consapevole che non sono esperto su questo, l'idea è di scindere la tua Entità dal dover conoscere la cosa concreta "componente", ma invece fare affidamento su una rappresentazione intermedia. Un esempio molto semplificato può essere che l'entità è un archivio di valori-chiave e il componente Salute genera una voce "health" -> 10
in quell'archivio. In questo modo, il mondo esterno può aggiungere componenti all'entità, senza che l'entità stessa debba sapere nulla su questi componenti.
Non sono sicuro di aver compreso correttamente l'idea dei guru dei giochi (le correzioni di qualcuno che conosce meglio sono sempre benvenute), ma ho l'impressione che con questo approccio sacrifichi molta sicurezza e ragionamento in fase di compilazione abilità come compromesso per ottenere un accoppiamento molto basso (da qui le buone opzioni di scripting con Lua e simili) e possibilità di implementazione potenzialmente più veloci (tramite allineamenti di memoria di questo componente per ottimizzare gli accessi alla memoria / alla cache). Supponendo che sia corretto, non potrei ancora rivendicare un modo per essere quello giusto. Tutti noi abbiamo le nostre preferenze e requisiti esterni che dovrebbero essere soddisfatti nel miglior modo possibile.