Comment puis-je expliquer l'utilité de l'héritage? [fermé]


16

Lorsque vous essayez d'expliquer le concept de l'héritage dans la POO, l'exemple commun est souvent l'exemple des mammifères. À mon humble avis, c'est vraiment un mauvais exemple, car cela amènera les débutants à utiliser ce concept dans le mauvais sens. Et de plus, ce n'est pas une conception commune à laquelle ils seront confrontés dans leur travail de conception au jour le jour.

Alors, quel sera un problème agréable, simple et concret qui sera résolu en utilisant l'héritage?


1
"l'exemple commun est souvent les mammifères"? Que voulez-vous dire? Pouvez-vous fournir un lien, une référence ou un devis pour cela?
S.Lott


3
Quel serait le meilleur exemple réel pour expliquer l'utilité du fonds fiduciaire pour les enfants Héritage = Bill Gates?
Martin Beckett

1
@Chris: en quoi cette question n'est-elle pas constructive? Êtes-vous en train de déclarer qu'un demandeur et 14 répondants perdent le temps de tout le monde?
Dan Dascalescu

@DanDascalescu - il a été fermé il y a deux ans en réponse à un drapeau indiquant "veuillez considérer la fermeture comme non constructive: à en juger par les réponses qui s'y accumulent, cela ressemble à une liste typique / question de sondage". Si vous pensez que c'est faux, modifiez-le pour qu'il soit clair et laissez la communauté décider via la file d'attente de réexamen.
ChrisF

Réponses:


15

Il n'y a rien de mal à un exemple purement académique comme les mammifères. J'aime aussi l'exemple rectangle / carré car il montre pourquoi les taxonomies du monde réel ne se traduisent pas toujours directement par la relation d'héritage que vous attendez.

À mon avis, l'exemple le plus canonique de tous les jours est une boîte à outils GUI. C'est quelque chose que tout le monde a utilisé, mais que les débutants peuvent ne pas avoir réfléchi à la façon dont ils travaillent sous le capot. Vous pouvez parler des comportements communs à tous les conteneurs, tous les widgets, événements, etc. sans nécessiter une connaissance détaillée d'une implémentation donnée.


5
+1 pour les boîtes à outils GUI ... et j'aime aussi l'exemple des formes, avec une forme comme base avec un dessin minimal () et les formes descendantes avec un dessin personnalisé () s.
yati sagade

14

Mon exemple réel est le modèle de domaine d'une simple application RH. Je dis que nous pouvons créer une classe de base appelée Employé , car bien sûr, les gestionnaires sont également des employés.

public class Employee
{
    public string FirstName { get; set; }

    public string LastName { get; set; }

    public int Code { get; set; }

    public string GetInsuranceHistory()
    {
        // Retrieving insurance history based on employee code.
    }
}

J'explique ensuite que les développeurs sont des employés , les testeurs sont des employés , les chefs de projet sont des employés . Ainsi, ils peuvent tous hériter de la classe des employés.


2
Pour montrer les avantages de l'héritage, il peut être intéressant de montrer également en quoi les développeurs diffèrent des employés. S'il n'y a pas de différence, il n'est pas du tout nécessaire de créer une classe Developer.
David

3
On dirait que ça Employeepourrait aussi être une abstractclasse.
StuperUser

+1 c'est l'exemple que la plupart de mes professeurs ont utilisé et j'ai vraiment aimé. cela avait un sens complet et donnait un exemple concret sur la façon d'utiliser l'héritage.
David Peterman

18
Sauf que cela ne fonctionne jamais en pratique car il y a toujours au moins une personne qui doit être à la fois a Developeret a Tester. Une autre situation similaire est une base de données de contacts où vous avez Customeret Supplier, mais comme toute personne qui a créé un tel système vous le dira, il y a toujours un cas où a Companyest les deux. C'est pourquoi la plupart de ces exemples vous conduisent dans la mauvaise direction.
Scott Whitlock

11

Encapsulez ce qui varie ... montrez-leur un modèle de méthode de modèle , cela démontre l'utilité de l'héritage en mettant un comportement commun dans une classe de base et en encapsulant un comportement variable dans des sous-classes.

UI controlset Streamssont également un très bon exemple de l'utilité de l'héritage.


Je pense que cette usine serait un meilleur exemple.
Let_Me_Be

1
@Let_Me_Be: Je pense que la relation entre une usine et l'héritage est de nature trop indirecte. Bien sûr, il produit des types concrets et renvoie des types abstraits / de base, mais il pourrait également renvoyer juste un type d'interface! À mon humble avis, ce n'est pas mieux que l'exemple animal classique.
Falcon

@Let_Me_Be: En outre, une fabrique abstraite est un exemple assez complexe impliquant différentes hiérarchies d'héritage (une pour les articles, une pour les usines). Je pense que c'est une bonne utilisation de l'héritage, mais pas un bon et simple exemple.
Falcon

3

Rappelles toi

Chaque instance d'un objet est un exemple concret de l'utilité de l'héritage!

Si vous voulez dire spécifiquement l' héritage de classe , vous êtes maintenant dans le monde des taxonomies, et celles-ci varieront considérablement en fonction des objectifs du système qui les utilise. L'exemple animaux / mammifères utilise une taxonomie commune et, espérons-le, familière de la biologie, mais elle est (comme vous l'avez mentionné) presque inutile pour la grande majorité des problèmes de programmation.

Essayez donc quelque chose d'universel: la notion de programme. Chaque programme démarre, s'exécute et se termine. Chaque programme a un nom et des paramètres de ligne de commande facultatifs. Ainsi, une classe Program de base serait très utile pour démarrer l'exécution, récupérer et traiter les arguments de ligne de commande, exécuter la logique principale et arrêter correctement.

C'est pourquoi tant de langages de programmation orientés objet fournissent une classe Program, ou quelque chose qui se comporte exactement comme une classe Program.


Donc, vous programmez un programme qui contient de nombreux programmes? :) D'après mon expérience, les objets de programmation sont presque toujours des singletons qui n'ont pas d'héritage, donc à mon humble avis, ils ne sont pas le meilleur exemple.
keppla

@keppla: avez-vous déjà utilisé Java ou .NET? .NET a une classe de programme explicite, Java est implicite. Ce ne sont pas des singletons
Steven A. Lowe

j'ai utilisé Java, autour de la version 1.4.2. À l'époque, il n'y avait que le vide principal statique, donc je suppose que cela a un peu changé. Quelle serait la raison typique d'avoir plus d'une instance de la classe Programm?
keppla

@keppla: le principal void statique de java fait implicitement que la classe d'entrée représente le programme. Chaque utilisateur qui exécute votre programme en crée une nouvelle instance. En ce moment, j'ai trois instances de Google Chrome en cours d'exécution, quatre documents Word, trois blocs-notes et deux explorateurs Windows. S'ils étaient tous des singletons, je ne serais jamais capable de faire ça.
Steven A. Lowe,

1
je pense que vous étirez un peu la définition. class Programm { public static void main(String[] args) { system.out.println('hello world'); }}est un programme Java minimal. Quand je l'appelle, il n'y a pas d'instance de programme. Le programme n'hérite de rien. Lorsque je démarre 3 processus (comme vous le faites avec crhome), il peut y avoir 3 programmes, mais dans leurs zones de mémoire individuelles, il n'y a toujours qu'un seul programme. À mon humble avis, singleton implique «une seule instance par processus», pas par machine. Si c'est le cas, il serait impossible de faire des singletons, rien ne vous empêche d'exécuter deux fois le code.
keppla

3

Je travaille avec des caméras au travail. Nous avons des appareils qui se connectent à différents modèles, nous avons donc une "classe de caméra" abstraite et chaque modèle hérite de cette classe pour prendre en charge des fonctionnalités spécifiques de cette caméra. C'est un exemple réel et pas difficile à comprendre.


2
Cela pourrait tomber en panne si vous en avez, par exemple un modèle qui est à la fois un Cameraet un Phone(comme nous le faisons tous dans nos poches maintenant). De quelle classe de base devrait-elle hériter? Ou ne devrait-il pas simplement implémenter les interfaces ICameraet IPhone? (ha ha)
Scott Whitlock

2
@Scott: Vous ne pouvez pas implémenter l'interface IPhone, ou vous serez poursuivi par Apple.
Mason Wheeler

3

Exemple d'éléments de chimie

Voici un autre exemple sorti de mon cerveau:

classe Element_
{
    double atomicWeight; // Poids atomique de l'élément
    double atomicNumber; // Numéro atomique de l'élément
    Propriétés de chaîne; // Propriétés de l'élément
    // Autres, le cas échéant
}


classe Isotope étend Element_ // Il peut exister des isotopes d'élément
{
    double demi-vie;
   // Autres le cas échéant

}

2
Bien que atomicNumber puisse (devrait?) Être probablement entier ...
Andrew

Je n'utiliserais pas l'héritage pour ça. isotopen'est pas un cas particulier de Elemenet. Je préfère avoir une Elementpropriété Isotope.
CodesInChaos

2

Les exemples du monde réel se trompent presque toujours parce qu'ils donnent des exemples où il y a toujours la possibilité que quelque chose soit à la fois TypeAet TypeBmais la hiérarchie d'héritage unique de nombreuses langues ne le permet pas.

Plus je programme, plus je m'éloigne de l'héritage.

Même le mot "hériter" n'est pas utilisé correctement ici. Par exemple, vous héritez d'environ 50% des traits de votre père et 50% des traits de votre mère. Vraiment, votre ADN est une composition de la moitié de l'ADN de votre père et de la moitié de l'ADN de votre mère. C'est parce que la biologie favorise la composition plutôt que l'héritage , et vous devriez aussi.

La simple mise en œuvre d'interfaces, ou mieux encore, le "typage du canard", plus l'injection de dépendances, est une bien meilleure chose que d'enseigner à des personnes novices en programmation orientée objet.


1

Je leur montrerais juste un exemple concret. Par exemple, dans la plupart des frameworks d'interface utilisateur, vous dérivez d'une sorte de classe "Dialog" ou "Window" ou "Control" pour créer la vôtre.


1

Un bon exemple est la fonction de comparaison dans le tri:

template<class T>
class CompareInterface {
public:
   virtual bool Compare(T t1, T t2) const=0;
};
class FloatCompare : public CompareInterface<float> { };
class CompareImplementation : public FloatCompare {
public:
   bool Compare(float t1, float t2) const { return t1<t2; }
};
template<class T>
void Sort(T*array, int size, CompareInterface<T> &compare);

Le seul problème est que les débutants pensent trop souvent que les performances sont plus importantes qu'un bon code ...


0

Mon exemple réel est un véhicule:

public class Vehicle
{
    public Vehicle(int doors, int wheels)
    {
        // I describe things that should be
        // established and "unchangeable" 
        // when the class is first "made"
        NumberOfDoors = doors;
        NumberOfWheels = wheels;
    }

    public void RollWindowsUp()
    {
        WindowsUp = true;
    }

    // I cover modifiers on properties to show
    // how to protect certain things from being
    // overridden
    public int NumberOfDoors { get; private set; }
    public int NumberOfWheels { get; private set; }

    public string Color { get; set; }
    public bool WindowsUp { get; set; }
    public int Speed { get; set; }
}

public class Car : Vehicle
{
    public Car : base(4, 4)
    {

    }
}

public class SemiTruck : Vehicle
{
    public SemiTruck : base(2, 18)
    {

    }
}

Cet exemple peut être aussi détaillé que vous le souhaitez, et il existe toutes sortes de propriétés attachées aux véhicules pour expliquer l'utilisation des modificateurs que vous voudrez peut-être enseigner.


2
J'ai toujours détesté utiliser des véhicules comme exemple, car cela ne permet pas à un nouveau programmeur de mieux comprendre comment l'héritage peut être utilisé pour améliorer le code. Les véhicules sont des machines extrêmement compliquées qui rappellent beaucoup d'idées non abstraites dans l'esprit du non-programmeur. Essayer de décrire cela dans le code fait croire au novice moyen que de nombreux détails sont laissés de côté et donne l'impression qu'il n'est pas plus proche de faire fonctionner quelque chose. Je le dis par expérience, car c'est exactement ce que j'ai ressenti quand quelqu'un a essayé d'utiliser des véhicules pour me l'expliquer.
riwalk

@ Stargazer712: J'utilise des véhicules principalement parce qu'ils peuvent être aussi compliqués ou aussi simples que vous le souhaitez. Je laisse le soin à l'instructeur de déterminer le niveau de son élève. J'ai expliqué la POO de base à ma femme (qui n'a aucune expérience en programmation) en utilisant les propriétés simples d'un véhicule décrivant les bases courantes. Tous les véhicules ont des portes, tous les véhicules ont des roues, etc. L'exemple d'objet ne peut pas être blâmé pour un mauvais plan de leçon.
Joel Etherton

votre femme n'essayait pas d'écrire du code. Je peux très dire en toute sécurité que des exemples de véhicules n'a rien fait pour me aider à comprendre l' héritage. Quoi que vous utilisiez pour décrire l'héritage, il doit être complet et pratique . Le but n'est pas de le décrire de telle manière qu'un non-programmeur puisse le comprendre. Le but est de le décrire de telle manière qu'un programmeur débutant puisse l' utiliser , et la seule façon de le faire est de montrer des exemples novices de la façon dont un programmeur professionnel l'utiliserait.
riwalk

@ Stargazer712: Je mettrais votre incapacité à comprendre initialement l'héritage sur un mauvais plan de leçon. J'ai également utilisé des véhicules pour expliquer l'héritage aux juniors avec qui je travaille, et je n'ai jamais eu de problème avec le concept. À mon avis, si un plan de leçon est complet et bien construit, un objet véhicule est à la fois complet et pratique. Un gars au hasard sur Internet ne changera rien à cela face à la trentaine de stagiaires et de développeurs juniors à qui j'ai enseigné la POO. Si vous n'aimez pas le véhicule, votez contre et continuez.
Joel Etherton

Comme vous le souhaitez ....
riwalk

0

Cet exemple sans mammifère, sans oiseau et sans poisson pourrait aider:

public abstract class Person {

    /* this contains thing all persons have, like name, gender, home addr, etc. */

    public Object getHomeAddr() { ... }

    public Person getName() { ... }

}

public class Employee extends Person{

    /* It adds things like date of contract, salary, position, etc */

    public Object getAccount() { ... }

}

public abstract class Patient extends Person {
    /* It adds things like medical history, etc */
}

alors

public static void main(String[] args) {

    /* you can send Xmas cards to patients and employees home addresses */

    List<Person> employeesAndPatients = Factory.getListOfEmployeesAndPatients();

    for (Person p: employeesAndPatients){
        sendXmasCard(p.getName(),p.getHomeAddr());
    }

    /* or you can proccess payment to employees */

    List<Employee> employees = Factory.getListOfEmployees();

    for (Employee e: employees){
        proccessPayment(e.getName(),e.getAccount());
    }       

}

REMARQUE: ne dites pas le secret: la personne étend le mammifère.


1
Fonctionne jusqu'à ce qu'un de vos employés soit également patient.
Scott Whitlock

Dans ce cas, je pense qu'il est plus logique de déclarer Patient et Employee en tant qu'interfaces au lieu de classes abstraites. Cela vous donne la possibilité de demander à Person d'implémenter plusieurs interfaces.
Jin Kim

@JinKim - Je suis totalement d'accord, c'est la meilleure approche.
Scott Whitlock

@JinKim Ils ne sont pas exclusifs. Vous traitez une personne comme un employé ou comme un patient à un moment donné, mais pas en même temps. Deux interfaces est OK, mais alors comment appelez-vous une classe concrète implémentant les deux, EmployeePatient? Combien de combinaisons aurez-vous?
Tulains Córdova

Vous pouvez appeler la classe concrète ce que vous voulez. Si le code prévoit uniquement de traiter avec des employés, vous déclarez la référence en tant qu'employé. (c.-à-d. Employé employé = nouvelle personne ();) Si le code ne s'attend qu'à traiter avec un patient, vous déclarez la référence en tant que patient. Vous souhaitez rarement déclarer une référence directement comme classe concrète.
Jin Kim

0

Que diriez-vous d'une hiérarchie d'expressions algébriques. Est bon car il comprend à la fois l'héritage et la composition:

+--------------------+------------------------+
| Expression         |<------------------+    |
+--------------------+----------+        |    |
| + evaluate(): int  |<---+     |        |    |
+--------------------+    |     |        |    |
          ^               |     |        |    |
          |               |     |        |    |
   +--------------+  +---------------+  +-------------+  ...
   | Constant     |  | Negation      |  | Addition    |
   +--------------+  +---------------+  +-------------+
   | -value: int  |  |               |  |             |
   +--------------+  +---------------+  +-------------+
   | +evaluate()  |  | +evaluate()   |  | +evaluate() |
   | +toString()  |  | +toString()   |  | +toString() |
   +--------------+  +---------------+  +-------------+

   Addition(Constant(5), Negation(Addition(Constant(3),Constant(2))))
   (5 + -(3 + 2)) = 0

À l'exception de l'expression racine Constant, toutes les autres expressions sont à la fois une expression et contiennent une ou plusieurs expressions.


-1

Je vais utiliser des oiseaux comme exemple

comme le poulet, le canard, l'aigle

Je vais expliquer que les deux ont des griffes, des coups de bec et des ailes mais leurs attributs sont différents.

Les poulets ne peuvent pas voler, ne peuvent pas nager, peuvent manger des vers, peuvent manger des céréales

Les canards ne peuvent pas voler, peuvent nager, peuvent manger des céréales, ne peuvent pas manger de vers

L'aigle peut voler, ne peut pas nager, peut manger des vers, ne peut pas manger de céréales


4
J'ai lu une fois que la composition et les interfaces sont probablement le meilleur moyen de transmettre ce type de concept, c'est-à-dire voler, nager, etc.
dreza

Un canard ne peut pas voler?!
Adam Cameron

-3

Votre rails-clone typique fournit de nombreux exemples pratiques : vous avez la classe de modèle de base (abstraite), qui encapsule toutes les manipulations de données et vous avez la classe de contrôleur de base, qui encapsule toutes les communications HTTP.


Voulez-vous expliquer pourquoi cette réponse est mauvaise?
keppla
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.