Domanda

In un progetto il nostro team sta utilizzando elenchi di oggetti per eseguire operazioni di massa su insiemi di dati che dovrebbero essere tutti elaborati in modo simile.In particolare, oggetti diversi dovrebbero idealmente agire allo stesso modo, cosa che si otterrebbe molto facilmente con il polimorfismo.Il problema che ho è che l'ereditarietà implica il è un relazione, piuttosto che ha un relazione.Ad esempio, diversi oggetti avere un contatore dei danni, ma per renderlo facile da usare in un elenco di oggetti, si potrebbe usare il polimorfismo, tranne che ciò implicherebbe un è un rapporto che non sarebbe vero.(Una persona non è un contatore dei danni.)

L'unica soluzione a cui riesco a pensare è che un membro della classe restituisca il tipo di oggetto corretto quando viene castato implicitamente invece di fare affidamento sull'ereditarietà.Sarebbe meglio rinunciare a è un / ha un ideale in cambio della facilità di programmazione?

Modificare:Per essere più specifici, sto usando C++, quindi l'uso del polimorfismo consentirebbe ai diversi oggetti di "agire allo stesso modo" nel senso che le classi derivate potrebbero risiedere all'interno di un unico elenco ed essere gestite da una funzione virtuale della classe base.L'uso di un'interfaccia (o la loro imitazione tramite ereditarietà) sembra una soluzione che sarei disposto a utilizzare.

È stato utile?

Soluzione

Ciò può essere ottenuto utilizzando l'ereditarietà multipla.Nel tuo caso specifico (C++), puoi utilizzare classi virtuali pure come interfacce.Ciò consente di avere ereditarietà multipla senza creare problemi di ambito/ambiguità.Esempio:

class Damage {
    virtual void addDamage(int d) = 0;
    virtual int getDamage() = 0;
};

class Person : public virtual Damage {
    void addDamage(int d) {
        // ...
        damage += d * 2;
    }

    int getDamage() {
        return damage;
    }
};

class Car : public virtual Damage {
    void addDamage(int d) {
        // ...
        damage += d;
    }

    int getDamage() {
        return damage;
    }
};

Ora sia Person che Car "is-a" Damage, ovvero implementano l'interfaccia Damage.L'uso di classi virtuali pure (in modo che siano come interfacce) è fondamentale e dovrebbe essere utilizzato frequentemente.Isola i cambiamenti futuri dall’alterare l’intero sistema.Per ulteriori informazioni, leggi il principio di apertura-chiusura.

Altri suggerimenti

Penso che dovresti implementare le interfacce per poter applicare il tuo ha un relazioni (lo sto facendo in C#):

public interface IDamageable
{
    void AddDamage(int i);
    int DamageCount {get;}
}

Potresti implementarlo nei tuoi oggetti:

public class Person : IDamageable

public class House : IDamageable

E saresti sicuro che la proprietà DamageCount abbia un metodo per consentirti di aggiungere danni, senza implicare che una persona e una casa siano correlate tra loro in una sorta di gerarchia.

Sono d'accordo con Jon, ma supponendo che tu abbia ancora bisogno di una classe di contro-danno separata, puoi fare:

class IDamageable {
  virtual DamageCounter* damage_counter() = 0;
};
class DamageCounter {
  ...
};

Ciascuna classe danneggiabile deve quindi fornire la propria funzione membro Damage_Counter().Lo svantaggio è che crea una tabella v per ogni classe danneggiabile.Puoi invece utilizzare:

class Damageable {
 public:
  DamageCounter damage_counter() { return damage_counter_; }
 private:
  DamageCounter damage_counter_;
};

Ma molte persone lo sono Non bello con ereditarietà multipla quando più genitori hanno variabili membro.

A volte vale la pena rinunciare all'ideale per il realistico.Se "farlo bene" causerebbe un grosso problema senza alcun beneficio reale, allora lo farei in modo sbagliato.Detto questo, penso spesso che valga la pena prendersi il tempo per farlo bene, perché l'ereditarietà multipla non necessaria aumenta la complessità, e questo Potere contribuiscono a rendere il sistema meno manutenibile.Devi davvero decidere cosa è meglio per la tua circostanza.

Un'opzione potrebbe essere quella di far sì che questi oggetti implementino a Damageable interfaccia, anziché ereditare da DamageCounter.In questo modo, una persona ha un contatore dei danni, ma È danneggiabile.(Spesso trovo che le interfacce abbiano molto più senso come aggettivi che come sostantivi.) Quindi potresti avere un'interfaccia di danno coerente su Damageable oggetti e non esporre che un contatore di danni è l'implementazione sottostante (a meno che non sia necessario).

Se vuoi seguire il percorso del modello (assumendo C++ o simili), potresti farlo con i mixin, ma può diventare brutto molto rapidamente se fatto male.

Questa domanda è davvero confusa :/

La tua domanda in grassetto è molto aperta e ha una risposta "dipende", ma il tuo esempio non fornisce molte informazioni sul contesto da cui stai chiedendo.Queste righe mi confondono;

insiemi di dati che dovrebbero essere trattati tutti in modo simile

Che modo?Gli insiemi vengono elaborati da una funzione?Un'altra lezione?Tramite una funzione virtuale sui dati?

In particolare, oggetti diversi dovrebbero idealmente agire allo stesso modo, cosa che si otterrebbe molto facilmente con il polimorfismo

L'ideale di "agire allo stesso modo" e il polimorfismo non sono assolutamente correlati.In che modo il polimorfismo lo rende facile da ottenere?

@Kevin

Normalmente quando parliamo di "è un" vs "ha un" stiamo parlando di ereditarietà vs composizione.

Ehm... il contatore dei danni sarebbe solo un attributo di una delle tue classi derivate e non verrebbe realmente discusso in termini di "Una persona è un segnalino danni" rispetto alla tua domanda.

Avere il segnalino danno come attributo non gli consente di diversificare oggetti con segnalini danno in una collezione.Ad esempio, una persona e un'auto potrebbero avere entrambe dei segnalini danno, ma tu non puoi averne uno vector<Person|Car> o a vector<with::getDamage()> o qualcosa di simile nella maggior parte delle lingue.Se hai una classe base Object comune, puoi inserirla in quel modo, ma non puoi accedere a getDamage() metodo in modo generico.

Questa era l'essenza della sua domanda, come l'ho letta."Devo violare is-a E has-a per il gusto di trattare certi oggetti come se fossero la stessa cosa, anche se non lo sono?"

Normalmente quando parliamo di "è un" vs "ha un" stiamo parlando di ereditarietà vs composizione.

Ehm... il contatore dei danni sarebbe solo un attributo di una delle tue classi derivate e non verrebbe realmente discusso in termini di "Una persona è un segnalino danni" rispetto alla tua domanda.

Guarda questo:

http://www.artima.com/designtechniques/compoinh.html

Il che potrebbe aiutarti lungo la strada.

@Derek:Dalla formulazione, ho pensato che fosse lì era una classe base, dopo aver riletto la domanda ora vedo a cosa intende arrivare.

"Farlo bene" avrà benefici a lungo termine, se non altro perché qualcuno che manterrà il sistema in seguito troverà più facile capire se è stato fatto bene all'inizio.

A seconda della lingua, potresti avere la possibilità di ereditarietà multipla, ma normalmente le interfacce semplici hanno più senso.Per "semplice" intendo creare un'interfaccia che non cerchi di essere troppo.Meglio avere molte interfacce semplici e alcune monolitiche.Naturalmente, c'è sempre un compromesso, e troppe interfacce probabilmente porterebbero a "dimenticarne" alcune...

@Andrea

L'ideale di "agire allo stesso modo" e il polimorfismo non sono assolutamente correlati.In che modo il polimorfismo lo rende facile da ottenere?

Tutti hanno, ad esempio, una funzione in comune.Chiamiamolo addDamage().Se vuoi fare qualcosa del genere:

foreach (obj in mylist)
    obj.addDamage(1)

Quindi è necessario un linguaggio dinamico oppure è necessario che si estendano da una classe genitore comune (o interfaccia).per esempio.:

class Person : DamageCounter {}
class Car : DamageCounter {}

foreach (DamageCounter d in mylist)
    d.addDamage(1)

Quindi puoi trattare Person E Car lo stesso in alcune circostanze molto utili.

Polimorfismo non richiede ereditarietà.Il polimorfismo è ciò che si ottiene quando più oggetti implementano la stessa firma (metodo) del messaggio.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top