Esiste uno schema migliore dell'eredità multipla qui?

2

Sto lavorando all'implementazione di un server per un gioco di grandi dimensioni con molti gametypes. Esistono diversi tipi di entità interabili: giocatori, mostri, oggetti, veicoli.

Tutte le entità condividono la stessa classe base (che è Cython):

cdef class Entity:
    cdef public int id
    cdef public double x,y,z,yaw,pitch,speed

    def __init__(self, int id, double x, double y, double z, double yaw, double pitch):
        self.id = id
        self.x = x
        self.y = y
        self.z = z
        self.yaw = yaw
        self.pitch = pitch
        self.speed = 0.1

Poi c'è una sottoclasse per ciascuno dei tipi di entità menzionati in precedenza, queste sottoclassi implementano i diversi pacchetti coinvolti per ciascuno di questi tipi.

Tutte le entità supportano la posizione e la rotazione, e tutte le entità possono in teoria supportare diversi comportamenti (e possono essere molto diversi a seconda del tipo di gioco) ma spesso non ne hanno bisogno. Potrebbero esserci circa 90 comportamenti diversi, quindi supportare tutto ciò sulla base di MonsterEntity / qualunque classe non abbia senso. Molti di questi comportamenti dipendono comunque da cose come la salute, quindi se non lo implemento in una classe base dovrei implementarlo in una sottoclasse .. ma ci sono così tanti comportamenti come quelli che sono correlati finire con un sacco di codice ripetuto in diverse sottoclassi o migliaia di linee solitamente non necessarie in una singola grande classe.

Ho deciso che una specie di composizione avrebbe molto più senso qui, quindi ho provato a usare l'ereditarietà multipla poiché Python lo gestisce abbastanza bene:

class EntityHealth(object):
    def __init__(self, maxhealth=20):
        self.health = maxhealth
        self.maxhealth = maxhealth

    def damage(self, amount):
        # damage the entity
        pass

    def setHealth(self, health):
        # set the entity's health
        pass

class EntityInventory(object):
    def __init__(self):
        self.inventory = Inventory()

    def dropAllItems(self):
        # do stuff
        pass

class SlayableMonster(Monster, EntityHealth, EntityInventory):
    def __init__(self, id, name, x, y, z, yaw, pitch):
        Monster.__init__(self, id, name, x, y, z, yaw, pitch, meta, uuid, skinblob)
        EntityHealth.__init__(self, 20)
        EntityInventory.__init__(self)

Ho sentito che le persone sostengono che l'ereditarietà multipla non dovrebbe mai essere usata, ma questo sembra un progetto molto conciso.

Alcuni pensieri ...

  • Tutte le classi di "comportamento" erediterebbero da un oggetto o un comportamento solo per estendere tale comportamento.
  • Il problema del rombo non dovrebbe mai accadere di conseguenza perché non avresti mai due classi che ereditano dalla stessa classe sull'albero entrambe applicate alla stessa entità.

Quindi i problemi MRO dovrebbero essere impossibili perché la progettazione è semplice, non esiste una gerarchia di ereditarietà complessa.

Questa è la soluzione migliore qui o c'è qualcosa di altrettanto / più conciso con gli stessi vantaggi? Voglio solo isolare il codice pertinente insieme, pur essendo in grado di scegliere quali entità hanno bisogno di quel tipo di comportamento.

    
posta Salis 28.07.2015 - 13:40
fonte

2 risposte

2

L'ereditarietà multipla è a volte la cosa giusta da fare.

Probabilmente non dovresti ereditare entrambi da Car e OilRig , ma ereditare da Walker e Talker può avere molto senso, in particolare se usare la delega introdurrebbe un sacco di metodi di delega banali che sono un dolore da mantenere. È particolarmente benevolo se rasenta l'emulazione di tratti o l'implementazione di più interfacce con implementazioni predefinite.

Sembra che sia esattamente quello che stai facendo qui. Certamente non hai nessuno dei problemi (selezione di metodi virtuali ereditati indirettamente nella configurazione diamante, layout interno indefinito di oggetti derivati) che ha causato così tanti problemi in C ++ e influenzato la saggezza convenzionale che dovresti mai fallo.

    
risposta data 28.07.2015 - 13:49
fonte
1

The diamond problem shouldn't ever happen as a result because you would never have two classes that inherit from the same class up the tree both applied to the same entity.

Mai dire mai. Scommetto che con tutti questi 90 comportamenti, avrai un problema con il diamante. Ancora peggio se non te ne accorgi.

Dal piccolo esempio, sembra che tu voglia veramente mixin . In questo modo, il comportamento sarebbe un campo all'interno della sottoclasse e la sottoclasse implementerebbe un'interfaccia di quel comportamento con l'implementazione semplicemente delegazione all'istanza all'interno del campo. Questo sarebbe troppo prolisso se la lingua non avesse un supporto esplicito per questo, ma potrebbe essere una valida alternativa per l'ereditarietà multipla.

    
risposta data 28.07.2015 - 14:31
fonte

Leggi altre domande sui tag