Hai già ricevuto alcune belle risposte, ma l'enorme elefante nella stanza della tua domanda è questo:
sentito da qualcuno che l'uso dell'ereditarietà deve essere evitato e dovremmo usare invece le interfacce
Come regola empirica, quando qualcuno ti dà una regola empirica, quindi ignoralo. Questo non vale solo per "qualcuno che ti dice qualcosa", ma anche per leggere cose su Internet. A meno che tu non lo sappia perché (e può davvero sostenerlo), tale consiglio è inutile e spesso molto dannoso.
Nella mia esperienza, i concetti più importanti e utili in OOP sono "accoppiamento basso" e "alta coesione" (le classi / gli oggetti conoscono il meno possibile l'uno dell'altro e ogni unità è responsabile del minor numero di cose possibile).
Accoppiamento basso
Ciò significa che qualsiasi "pacchetto di roba" nel codice dovrebbe dipendere il meno possibile da ciò che lo circonda. Questo vale per le classi (design della classe) ma anche per gli oggetti (implementazione effettiva), i "file" in generale (cioè il numero di#include
elettronica per singolo .cpp
file, il numero di import
per .java
file singolo e così via).
Un segno che due entità sono accoppiate è che una di esse si romperà (o dovrà essere cambiata) quando l'altra viene cambiata in qualche modo.
L'ereditarietà aumenta l'accoppiamento, ovviamente; la modifica della classe di base modifica tutte le sottoclassi.
Le interfacce riducono l'accoppiamento: definendo un contratto chiaro e basato sul metodo, è possibile modificare liberamente qualsiasi cosa su entrambi i lati dell'interfaccia, purché non si modifichi il contratto. (Si noti che "interfaccia" è un concetto generale, Javainterface
classi astratte o C ++ sono solo dettagli di implementazione).
Alta coesione
Ciò significa che ogni classe, oggetto, file ecc. Deve essere interessato o responsabile il meno possibile. Vale a dire, evitare grandi classi che fanno un sacco di cose. Nel tuo esempio, se le tue armi hanno aspetti completamente separati (munizioni, comportamento al fuoco, rappresentazione grafica, rappresentazione dell'inventario ecc.), Puoi avere classi diverse che rappresentano esattamente una di quelle cose. La principale classe di armi si trasforma quindi in un "detentore" di quei dettagli; un oggetto arma è quindi poco più di qualche puntatore a quei dettagli.
In questo esempio, ti assicureresti che la tua classe che rappresenta il "Comportamento al fuoco" sappia quanto meno umanamente possibile sulla classe principale di armi. In modo ottimale, niente di niente. Ciò significherebbe, ad esempio, che potresti dare "Comportamento al fuoco" a chiunque oggetto nel tuo mondo (torrette, vulcani, PNG ...) con un semplice schiocco di un dito. Se ad un certo punto vuoi cambiare il modo in cui le armi sono rappresentate nell'inventario, allora puoi semplicemente farlo - solo la tua classe di inventario lo sa.
Un segno che un'entità non è coesiva è se diventa sempre più grande, ramificandosi in più direzioni contemporaneamente.
L'ereditarietà mentre la descrivi diminuisce la coesione: le tue classi di armi sono, alla fine della giornata, grossi pezzi che gestiscono tutti i tipi di aspetti diversi e non correlati delle tue armi.
Le interfacce aumentano indirettamente la coesione dividendo chiaramente le responsabilità tra le due parti dell'interfaccia.
cosa fare adesso
Non ci sono ancora regole rigide e veloci, tutto questo è solo una guida. In generale, come menzionato dall'utente TKK nella sua risposta, l'eredità viene insegnata molto a scuola e nei libri; sono le cose fantasiose su OOP. Le interfacce sono entrambe probabilmente più noiose da insegnare, e anche (se si superano esempi banali) un po 'più difficili, aprendo il campo dell'iniezione di dipendenza, che non è così netta come l'eredità.
Alla fine della giornata, il tuo schema basato sull'ereditarietà è ancora meglio che non avere alcun progetto OOP chiaro. Quindi sentiti libero di attenerci. Se lo desideri, puoi ruminare / google un po 'su Low Coupling, High Cohesion e vedere se desideri aggiungere quel tipo di pensiero al tuo arsenale. Puoi sempre rifattorizzare per provarlo, se lo desideri, in seguito; oppure prova approcci basati sull'interfaccia sul tuo prossimo nuovo più grande modulo di codice.