Question

Dans un projet, notre équipe utilise des listes d'objets pour effectuer des opérations de masse sur des ensembles de données qui doivent toutes être traitées de la même manière.En particulier, différents objets agiraient idéalement de la même manière, ce qui serait très facilement réalisé avec le polymorphisme.Le problème que j'ai avec cela est que l'héritage implique le est un relation, plutôt que la a un relation.Par exemple, plusieurs objets avoir un compteur de dégâts, mais pour rendre cela facile à utiliser dans une liste d'objets, le polymorphisme pourrait être utilisé - sauf que cela impliquerait un est un relation qui ne serait pas vraie.(Une personne n'est pas un compteur de dégâts.)

La seule solution à laquelle je puisse penser est qu'un membre de la classe renvoie le type d'objet approprié lorsqu'il est implicitement converti au lieu de s'appuyer sur l'héritage.Serait-il préférable de renoncer au est un / a un idéal en échange d'une facilité de programmation ?

Modifier:Pour être plus précis, j'utilise C++, donc l'utilisation du polymorphisme permettrait aux différents objets "d'agir de la même manière" dans le sens où les classes dérivées pourraient résider dans une seule liste et être exploitées par une fonction virtuelle de la classe de base.L'utilisation d'une interface (ou leur imitation via l'héritage) semble être une solution que je serais prêt à utiliser.

Était-ce utile?

La solution

Cela peut être accompli en utilisant l'héritage multiple.Dans votre cas spécifique (C++), vous pouvez utiliser des classes virtuelles pures comme interfaces.Cela vous permet d'avoir plusieurs héritages sans créer de problèmes de portée/ambiguïté.Exemple:

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;
    }
};

Désormais, la personne et la voiture « est un » dommage, ce qui signifie qu'elles implémentent l'interface Damage.L'utilisation de classes virtuelles pures (afin qu'elles soient comme des interfaces) est essentielle et doit être utilisée fréquemment.Cela empêche les changements futurs de modifier l’ensemble du système.Renseignez-vous sur le principe ouvert-fermé pour plus d'informations.

Autres conseils

Je pense que vous devriez implémenter des interfaces pour pouvoir appliquer votre a un relations (je fais cela en C#):

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

Vous pouvez implémenter cela dans vos objets :

public class Person : IDamageable

public class House : IDamageable

Et vous seriez sûr que la propriété DamageCount dispose d'une méthode pour vous permettre d'ajouter des dégâts, sans impliquer qu'une personne et une maison sont liées les unes aux autres dans une sorte de hiérarchie.

Je suis d'accord avec Jon, mais en supposant que vous ayez toujours besoin d'une classe de compteur de dégâts distincte, vous pouvez faire :

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

Chaque classe endommageable doit alors fournir sa propre fonction membre Damage_counter().L'inconvénient est que cela crée une table virtuelle pour chaque classe endommageable.Vous pouvez à la place utiliser :

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

Mais beaucoup de gens sont Pas cool avec héritage multiple lorsque plusieurs parents ont des variables membres.

Parfois, cela vaut la peine d'abandonner l'idéal pour le réalisme.Si « le faire correctement » sans réel avantage doit causer un énorme problème, alors je le ferais mal.Cela dit, je pense souvent que cela vaut la peine de prendre le temps de bien faire les choses, car l'héritage multiple inutile augmente la complexité, et cela peut contribuent à ce que le système soit moins maintenable.Vous devez vraiment décider de ce qui convient le mieux à votre situation.

Une option serait que ces objets implémentent un Damageable interface, plutôt que d'hériter de DamageCounter.De cette façon, une personne a un compteur de dégâts, mais est endommageable.(Je trouve souvent que les interfaces ont beaucoup plus de sens en tant qu'adjectifs qu'en tant que noms.) Vous pourriez alors avoir une interface de dégâts cohérente sur Damageable objets, et ne pas exposer qu'un compteur de dommages est l'implémentation sous-jacente (sauf si vous en avez besoin).

Si vous souhaitez emprunter la voie des modèles (en supposant que C++ ou similaire), vous pouvez le faire avec des mixins, mais cela peut devenir moche très rapidement si c'est mal fait.

Cette question est vraiment déroutante :/

Votre question en gras est très ouverte et a une réponse "ça dépend", mais votre exemple ne donne pas vraiment beaucoup d'informations sur le contexte à partir duquel vous posez la question.Ces lignes me confondent ;

ensembles de données qui doivent tous être traités de la même manière

Quel voie?Les ensembles sont-ils traités par une fonction ?Un autre cours ?Via une fonction virtuelle sur les données ?

En particulier, différents objets agiraient idéalement de la même manière, ce qui serait très facilement réalisé avec le polymorphisme.

L’idéal du « faire pareil » et le polymorphisme n’ont absolument aucun rapport.Comment le polymorphisme facilite-t-il sa réalisation ?

@Kévin

Normalement, lorsque nous parlons de « est un » et « a un », nous parlons d'héritage et de composition.

Euh... le compteur de dégâts serait simplement un attribut de l'une de vos classes dérivées et ne serait pas vraiment discuté en termes de « Une personne est un compteur de dégâts » par rapport à votre question.

Avoir le compteur de dégâts comme attribut ne lui permet pas de diversifier les objets avec des compteurs de dégâts dans une collection.Par exemple, une personne et une voiture peuvent toutes deux avoir des marqueurs de dégâts, mais vous ne pouvez pas avoir de marqueurs de dégâts. vector<Person|Car> ou un vector<with::getDamage()> ou quelque chose de similaire dans la plupart des langues.Si vous avez une classe de base Object commune, vous pouvez les déplacer de cette manière, mais vous ne pouvez pas accéder au getDamage() méthode de manière générique.

C'était l'essentiel de sa question, telle que je l'ai lue."Dois-je violer is-a et has-a dans le but de traiter certains objets comme s'ils étaient identiques, même s'ils ne le sont pas ?"

Normalement, lorsque nous parlons de « est un » et « a un », nous parlons d'héritage et de composition.

Euh... le compteur de dégâts serait simplement un attribut de l'une de vos classes dérivées et ne serait pas vraiment discuté en termes de « Une personne est un compteur de dégâts » par rapport à votre question.

Regarde ça:

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

Ce qui pourrait vous aider en cours de route.

@Derek:D'après le libellé, j'ai supposé que était un cours de base, après avoir relu la question, je vois un peu maintenant où il veut en venir.

"Faire les choses correctement" aura des avantages à long terme, ne serait-ce que parce que quelqu'un qui maintiendra le système plus tard comprendra plus facilement si cela a été bien fait au départ.

Selon la langue, vous pouvez avoir la possibilité d'hériter plusieurs fois, mais les interfaces simples sont généralement les plus logiques.Par "simple", j'entends créer une interface qui n'essaye pas d'en faire trop.Mieux vaut avoir beaucoup d’interfaces simples et quelques interfaces monolithiques.Bien sûr, il y a toujours un compromis, et trop d'interfaces conduiraient probablement à "oublier" certaines d'entre elles...

@André

L’idéal du « faire pareil » et le polymorphisme n’ont absolument aucun rapport.Comment le polymorphisme facilite-t-il sa réalisation ?

Ils ont tous, par exemple, une fonction en commun.Appelons-le addDamage().Si vous voulez faire quelque chose comme ceci :

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

Ensuite, vous avez besoin soit d'un langage dynamique, soit d'une extension à partir d'une classe parent (ou interface) commune.par exemple.:

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

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

Ensuite, vous pourrez traiter Person et Car la même chose dans certaines circonstances très utiles.

Polymorphisme ne nécessite pas d'héritage.Le polymorphisme est ce que vous obtenez lorsque plusieurs objets implémentent la même signature de message (méthode).

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top