Pourquoi utiliser l'héritage? [fermé]

je sais que la question a été discuté avant , mais il semble toujours sous l'hypothèse que l'héritage est au moins parfois préférable à la composition. J'aimerais remettre en question cette hypothèse dans l'espoir de mieux comprendre.

ma question Est la suivante: depuis vous pouvez accomplir n'importe quoi avec la composition d'objet que vous pouvez avec l'héritage classique et depuis classique l'héritage est très souvent abusé[1] et depuis la composition de l'objet vous donne la flexibilité de changer la durée d'exécution de l'objet délégué, pourquoi le jamais utiliser l'héritage classique?

je peux en quelque sorte comprendre pourquoi vous recommanderiez l'héritage dans certaines langues comme Java et C++ qui n'offrent pas une syntaxe commode pour la délégation. Dans ces langues, vous pouvez économiser beaucoup de taper en utilisant l'héritage s'il n'est pas clairement incorrect de le faire. Mais d'autres langues comme L'Objectif C et Ruby offrent à la fois l'héritage classique et syntaxe très conviviale pour la délégation. Le langage de programmation Go est le seul langage qui, à ma connaissance, a décidé que l'héritage classique est plus de problèmes qu'il n'en vaut la peine et soutient seulement la Délégation pour la réutilisation de code.

une autre façon d'énoncer ma question est celle-ci: même si vous savez que classique l'héritage n'est pas incorrect pour mettre en œuvre un certain modèle, est-ce une raison suffisante pour l'utiliser au lieu de la composition?

[1] de nombreuses personnes utilisent l'héritage classique pour atteindre le polymorphisme au lieu de laisser leurs classes mettre en œuvre une interface. Le but de l'héritage est la réutilisation de code, pas le polymorphisme. De plus, certaines personnes utilisent l'héritage pour modeler leur compréhension intuitive d'une relation "is-a qui peut souvent être problématique .

mise à Jour

je veux juste clarifier ce que je veux dire exactement quand je parle de l'héritage:

je parle de le genre d'héritage par lequel une classe hérite d'une classe de base partiellement ou entièrement implémentée . Je suis pas parler d'hériter d'une classe de base purement abstraite qui revient à la même chose que la mise en œuvre d'une interface, que je pour l'enregistrement je ne prétends pas contre.

Update 2

je comprends que l'héritage est le seul moyen d'atteindre le polymorphisme I C++. Dans ce cas, il est évident pourquoi vous devez l'utiliser. Ma question se limite donc à des langages comme Java ou Ruby qui offrent des moyens distincts de réaliser le polymorphisme (interfaces et typage du canard, respectivement).

57
demandé sur Community 2010-07-28 13:48:44

13 réponses

si vous déléguez tout ce que vous n'avez pas explicitement dépassé à un autre objet implémentant la même interface (l'objet" base"), alors vous avez en gros un héritage Greenspunned en plus de la composition, mais (dans la plupart des langues) avec beaucoup plus de verbosité et de boilerplate. Le but de l'utilisation de la composition au lieu de l'héritage est de sorte que vous ne pouvez déléguer que les comportements que vous voulez déléguer.

si vous voulez que l'objet utilise tout le comportement de la classe de base à moins qu'elle ne soit explicitement dépassée, alors l'héritage est la manière la plus simple, la moins verbeuse et la plus directe de l'exprimer.

9
répondu dsimcha 2010-07-28 13:38:40

le but de l'héritage est la réutilisation de code, pas le polymorphisme.

C'est votre erreur fondamentale. Presque exactement le contraire qui est vrai. Le principal but de l'héritage (public) est de modéliser les relations entre les classes en question. Le polymorphisme en est une grande partie.

utilisé correctement, l'héritage ne consiste pas à réutiliser le code existant. Il s'agit plutôt d'être utilisé par code existant. C'est-à-dire, si vous avez un code existant qui peut travailler avec la classe de base existante, lorsque vous dériver une nouvelle classe à partir de cette classe de base existante que d'autres codes peuvent désormais automatiquement avec votre nouvelle classe dérivée.

il est possible d'utiliser l'héritage pour réutiliser le code, mais quand/si vous le faites, il devrait normalement être privé héritage pas l'héritage public. Si le langage que vous utilisez prend en charge délégation il y a de bonnes chances que vous ayez rarement de bonnes raisons d'utiliser l'héritage privé. OTOH, l'héritage privé supporte quelques choses que la délégation (normalement) ne supporte pas. En particulier, même si le polymorphisme est une préoccupation décidément secondaire dans ce cas, il peut toujours être une préoccupation -- c.-à-d., avec l'héritage privé, vous pouvez commencer à partir d'une classe de base qui est presque ce que vous voulez, et (en supposant qu'il le permet) remplacer les pièces qui ne sont pas tout à fait droit.

avec délégation votre seul choix réel est d'utiliser la classe existante exactement telle qu'elle est. S'il ne fait pas tout à fait ce que vous voulez, votre seul véritable choix est d'ignorer complètement cette fonctionnalité, et de la ré-implémenter à partir de la base. Dans certains cas, ce n'est pas une perte, mais dans d'autres, c'est assez important. Si d'autres parties de la classe de base utilisent la fonction polymorphique, l'héritage privé vous permet de passer outre seulement le fonction polymorphe, et les autres parties vont utiliser votre substituée fonction. Avec delegation, vous ne pouvez pas facilement brancher votre nouvelle fonctionnalité de sorte que d'autres parties de la classe de base existante utiliseront ce que vous avez dépassé.

43
répondu Jerry Coffin 2010-07-28 15:23:59

la principale raison d'utiliser l'héritage est pas comme une forme de composition - il est donc vous pouvez obtenir un comportement polymorphe. Si vous n'avez pas besoin de polymorphisme, vous ne devriez probablement pas utiliser l'héritage, du moins en C++.

16
répondu 2010-07-28 09:52:32

L'héritage doit être préféré si:

  1. vous devez exposer toute L'API de la classe que vous étendez (avec délégation, vous aurez besoin d'écrire beaucoup de méthodes de délégation) et votre langue n'offre pas une façon simple de dire "déléguer toutes les méthodes inconnues à".
  2. vous devez accéder à des champs/méthodes protégés pour les langues qui n'ont pas le concept de" friends "
  3. les avantages de la délégation est quelque peu réduite si votre langue permet le multi héritage
  4. vous n'avez généralement pas besoin de délégation du tout si votre langue permet d'hériter dynamiquement d'une classe ou même d'une instance à l'exécution. Vous n'en avez pas besoin si vous pouvez contrôler les méthodes qui sont exposées (et comment ils sont exposés) en même temps.

Ma conclusion: la Délégation est une solution de contournement pour un bug dans un langage de programmation.

6
répondu Aaron Digulla 2010-07-28 09:56:16

tout le monde sait que le polymorphisme est un grand avantage de l'héritage. Un autre avantage que je trouve dans l'héritage est que cela aide à créer la réplique du monde réel. Par exemple dans le salaire roll, nous traitons les gestionnaires de développeurs ,bureau garçons etc. si on hérite de tous ces cours avec un employé super classe. il fait de notre programme plus compréhensible dans le contexte du monde réel que toutes ces classes sont essentiellement employés. Et une chose de plus classes non seulement contiennent des méthodes qu'ils contiennent aussi attribut. Donc, si nous contiennent des attributs génériques à l'employé l'Employé classe comme numéro de sécurité sociale âge etc. cela permettrait une meilleure réutilisation du code, une plus grande clarté conceptuelle et, bien sûr, un polymorphisme. Cependant, tout en utilisant les choses de l'héritage, nous devons garder à l'esprit le principe de base de la conception "identifier les aspects de votre application qui varient et les séparer de ces aspects qui changent". Vous ne devriez jamais mettre en œuvre ces aspects de l'application qui changent par héritage plutôt utiliser composition. Et pour les aspects qui ne sont pas changeables u devrait utiliser l'héritage naturellement si une évidente" est une " relation se trouve.

6
répondu faisalbhagat 2012-11-25 15:44:07

je pense toujours à deux fois avant d'utiliser l'héritage car il peut devenir difficile rapide. Cela dit, il existe de nombreux cas où il produit simplement le code le plus élégant.

4
répondu ChaosPandion 2010-07-28 13:41:31
Les Interfaces

définissent seulement ce qu'un objet peut faire et non comment. Donc, en termes simples, les interfaces ne sont que des contrats. Tous les objets qui implémentent l'interface devront définir leur propre implémentation du contrat. Dans le monde pratique, cela vous donne separation of concern . Imaginez-vous en train d'écrire une application qui a besoin de traiter avec divers objets que vous ne connaissez pas à l'avance, encore vous devez traiter avec eux, la seule chose que vous savez est ce que toutes les choses différentes que ces objets sont supposés faire. Vous définirez une interface et mentionnerez toutes les opérations dans le contrat. Maintenant, vous allez écrire votre application contre cette interface. Plus tard, celui qui veut tirer parti de votre code ou de l'application devra implémenter l'interface sur l'objet pour le faire fonctionner avec votre système. Votre interface forcera leur objet à définir comment chaque opération définie dans le contrat est censée être effectuée. De cette façon, n'importe qui peut écrire des objets qui implémentent l'interface, afin de les avoir parfaitement l'adapter à votre système et tout ce que vous savez est ce qui doit être fait et c'est l'objet qui doit définir comment il est fait.

Dans le monde réel développement de cette la pratique est généralement connu comme Programming to Interface and not to Implementation .

les Interfaces ne sont que des contrats ou des signatures et ils ne savent pas rien sur les implémentations.

codage par interface, le code client tient toujours un objet D'Interface qui est fourni par une usine. Toute instance retournée par l'usine serait de type Interface que n'importe quelle classe candidate d'usine doit avoir implémentée. De cette façon, le programme client ne s'inquiète pas de la mise en œuvre et la signature de l'interface détermine ce que toutes les opérations peuvent être faites. Ceci peut être utilisé pour changer le comportement d'un programme à l'exécution. Il vous aide également à écrire de bien meilleurs programmes du point de vue de la maintenance.

Voici un exemple de base pour vous.

public enum Language
{
    English, German, Spanish
}

public class SpeakerFactory
{
    public static ISpeaker CreateSpeaker(Language language)
    {
        switch (language)
        {
            case Language.English:
                return new EnglishSpeaker();
            case Language.German:
                return new GermanSpeaker();
            case Language.Spanish:
                return new SpanishSpeaker();
            default:
                throw new ApplicationException("No speaker can speak such language");
        }
    }
}

[STAThread]
static void Main()
{
    //This is your client code.
    ISpeaker speaker = SpeakerFactory.CreateSpeaker(Language.English);
    speaker.Speak();
    Console.ReadLine();
}

public interface ISpeaker
{
    void Speak();
}

public class EnglishSpeaker : ISpeaker
{
    public EnglishSpeaker() { }

    #region ISpeaker Members

    public void Speak()
    {
        Console.WriteLine("I speak English.");
    }

    #endregion
}

public class GermanSpeaker : ISpeaker
{
    public GermanSpeaker() { }

    #region ISpeaker Members

    public void Speak()
    {
        Console.WriteLine("I speak German.");
    }

    #endregion
}

public class SpanishSpeaker : ISpeaker
{
    public SpanishSpeaker() { }

    #region ISpeaker Members

    public void Speak()
    {
        Console.WriteLine("I speak Spanish.");
    }

    #endregion
}

alt texte http://ruchitsurati.net/myfiles/interface.png

3
répondu this. __curious_geek 2010-07-28 10:33:28

Qu'en est-il du modèle de méthode de modèle? Disons que vous avez une classe de base avec des tonnes de points pour des politiques personnalisables, mais un modèle de stratégie n'a pas de sens pour au moins une des raisons suivantes:

  1. les politiques personnalisables doivent connaître la classe de base, ne peuvent être utilisées qu'avec la classe de base et n'ont de sens dans aucun autre contexte. Utiliser la stratégie à la place est faisable mais une PITA parce que les deux la classe de base et la classe politique doivent avoir des références à l'autre.

  2. les politiques sont liées les unes aux autres en ce sens qu'il ne serait pas logique de les combiner librement. Ils n'ont de sens que dans un sous-ensemble très limité de toutes les combinaisons possibles.

2
répondu dsimcha 2010-07-28 13:33:55

vous avez écrit:

[1] Beaucoup de gens utilisent le classique hérédité pour atteindre le polymorphisme au lieu de laisser leurs classes implémenter une interface. Le but de l'héritage est la réutilisation de code, pas polymorphisme. En outre, certaines personnes utiliser l'héritage pour modéliser leur la compréhension intuitive de type "est-un" relations qui peuvent souvent être problématique.

dans la plupart des langues, la ligne entre " mise en œuvre une interface " et "dériver une classe d'une autre" est très mince. En fait, dans des langages comme C++, si vous dérivez une classe B d'une classe A, et A est une classe qui se compose uniquement de méthodes virtuelles pures, vous êtes implémentant une interface.

l'Héritage de l'interface de la réutilisation , pas la mise en œuvre de la réutilisation . C'est et non à propos de la réutilisation de code, comme vous l'avez écrit ci-dessus.

L'héritage, comme vous le faites remarquer à juste titre, est destiné à modeler une relation est-A (le fait que beaucoup de gens se trompent n'a rien à voir avec l'héritage en soi). Vous pouvez aussi dire "se comporte-comme-A". Cependant, ce n'est pas parce que quelque chose a une relation IS-A avec quelque chose d'autre que cela signifie qu'il utilise le même code (ou même similiar) pour accomplir cette relation.

comparez cet exemple de C++ qui implémente différentes façons de produire des données; deux classes utilisent

struct Output {
  virtual bool readyToWrite() const = 0;
  virtual void write(const char *data, size_t len) = 0;
};

struct NetworkOutput : public Output {
  NetworkOutput(const char *host, unsigned short port);

  bool readyToWrite();
  void write(const char *data, size_t len);
};

struct FileOutput : public Output {
  FileOutput(const char *fileName);

  bool readyToWrite();
  void write(const char *data, size_t len);
};

imaginez si C'était Java. 'Output 'n'était pas une structure, mais une'interface'. Il pourrait être appelé 'Écriture'. Au lieu de "Production" vous diriez 'implémente accessible en Écriture'. Quelle est la différence en ce qui concerne le design?

néant.

0
répondu Frerich Raabe 2010-07-28 10:14:22

la principale utilité de L'héritage classique est si vous avez un certain nombre de classes liées qui auront la même logique pour les méthodes qui opèrent sur les variables/propriétés d'instance.

il y a vraiment 3 façons de gérer cela:

  1. héritage.
  2. dupliquer le code ( code odeur "code dupliqué").
  3. Déplacer la logique à une autre classe (odeurs de code "Paresseux La classe", "l'Homme du Milieu," Message "Chaînes" et/ou "Inappropriés Intimité").

maintenant, il peut y avoir un mauvais usage de l'héritage. Par exemple, Java a les classes InputStream et OutputStream . Ces sous-classes sont utilisées pour lire/écrire des fichiers, des sockets, des tableaux, des chaînes, et plusieurs sont utilisées pour envelopper d'autres flux d'entrées/sorties. En se basant sur ce qu'ils font, ils auraient dû être des interfaces plutôt que des classes.

0
répondu Powerlord 2010-07-28 14:01:39

un des moyens les plus utiles que je vois pour utiliser l'héritage est dans les objets GUI.

0
répondu Brad Barker 2010-07-28 14:18:46

quand vous avez demandé:

même si vous savez que l'héritage classique n'est pas incorrect pour mettre en œuvre un certain modèle, est-ce une raison suffisante pour l'utiliser à la place de la composition?

La réponse est non. Si le modèle est incorrect (en utilisant l'héritage), qu'il est erroné d'utiliser quoi qu'il arrive.

voici quelques problèmes avec l'héritage que j'ai vu:

  1. Toujours avoir à tester le type de durée d'exécution des pointeurs de classe dérivés pour voir si elles peuvent être lancées vers le haut (ou vers le bas aussi).
  2. ce "test" peut être réalisé de différentes façons. Vous pouvez avoir une sorte de méthode virtuelle qui retourne un identificateur de classe. Ou à défaut, vous devrez peut-être implémenter RTTI (Run time type identification) (au moins en C/C++), ce qui peut vous donner un succès de performance.
  3. Les types de classe
  4. qui ne parviennent pas à obtenir "cast" up peuvent être potentiellement problématiques.
  5. il y a plusieurs façons de faire monter et descendre le type de classe dans l'arbre de l'héritage.
0
répondu C Johnson 2010-07-28 22:54:36

quelque chose qui n'est pas tout à fait OOP mais pourtant, la composition signifie généralement un cache-miss supplémentaire. Cela dépend, mais avoir les données plus proche est un plus.

en général, je refuse d'aller dans des combats religieux, en utilisant votre propre jugement et le style est le meilleur que vous pouvez obtenir.

0
répondu bestsss 2011-02-09 10:15:48