web-dev-qa-db-fra.com

Quel est un bon exemple pour l'héritage de classe?

J'écris de la documentation pour un langage orienté objet, et je me demande quel type de classe serait un bon exemple pour l'héritage.

Quelques exemples courants:

class Person {
}
class Employee extends Person {
}

Actuellement, mon préféré, mais je n'aime pas Personne-> Employé parce que "Employé" n'a pas vraiment l'air amusant.

class Bicycle {
}
class MountainBike extends Bicycle {
}

J'ai trouvé cela dans certains tutoriel Java , mais les attributs qu'un vélo devrait avoir ne sont pas vraiment évidents.

class Animal {
}
class Bird extends Animal {
}

Identique au vélo.

class A {
}
class B extends A {
}

Trop abstrait. Le problème principal est qu'une telle classe aura besoin d'attributs et de méthodes encore plus abstraits.

Quelqu'un a-t-il un meilleur exemple de hiérarchie de classes simple?

26
Tim Jansen

La classe Animal est l'exemple classique de l'héritage de classe pour plusieurs raisons.

Premièrement, il existe des moyens évidents d'étendre la classe animale sous-jacente. Vous commencerez probablement avec des sous-classes telles que les mammifères, les oiseaux, les crustacés, etc. 

Certaines classes, telles que Mammal, étendront Animal en ajoutant des attributs assez évidents ("Warm-Blooded", etc.). 

D'autres problèmes, plus problématiques, qui sont assez fréquents dans le développement d'une hiérarchie de classes sont assez évidents si vous illustrez avec l'héritage animal - et ceci est un bon à des fins d'explication. Les oiseaux volent, non? Eh bien, pas tous les oiseaux ... Alors, comment représentez-vous le vol? Il existe bien sûr des solutions classiques et une mine d'informations de discussion en ligne sur la manière de résoudre les problèmes et les compromis que chaque solution introduit.

Par conséquent, je recommanderais vivement d'utiliser "Animal" comme exemple en raison de la richesse des informations et des exemples disponibles.

12
Mark Brittingham

J'aime la hiérarchie Stream. L'idée est que tout élément peut utiliser un flux sans se soucier du type de flux dont il s'agit et que des sous-classes individuelles gèrent le stockage différemment (par exemple, NetworkStream, MemoryStream et FileStream dans .NET).

Si vous êtes intéressé par les interfaces, alors IEnumerable<T> dans .NET en est un excellent. Vous pouvez parcourir toute collection sans se soucier de la structure de données sous-jacente.

18
Jon Skeet

Les pièces automatiques peuvent être intéressantes, par exemple, vous pourriez avoir

class part
{
    OEM
    Manufacturer
    Number
    Description
}

class Tire extends Part
{
   Speed
   Rating

}
17
JoshBerke

Je suis d'accord avec Jon Skeet sur son exemple de flux. Ce n'est peut-être pas parfait, mais il a un avantage sur la plupart des exemples ici:

C'est réaliste

Vélos, personnes ou animaux, formes ou armes ne seraient tout simplement pas modelés par héritage dans des projets réels. (les formes en particulier sont carrément dangereuses, parce que ça ne marche pas .)

C'est ma bête noire avec héritage. Il est trop souvent enseigné comme quelque chose qui doit doit être utilisé pour exprimer chaque hiérarchie que vous pouvez trouver. Un employé est une personne, non? Donc, la classe Employee doit hériter d'une classe Person. Mais une personne est aussi une LivingCreature, il est donc préférable d’avoir l’une de ces classes. Et une LivingCreature est aussi un organisme, nous avons donc une autre classe. Et un organisme, c'est ... n'hésitez pas à continuer.

Je pense que ce serait bien si quelqu'un, quelque part, enseignait effectivement l'héritage en expliquant quand il devrait être utilisé, et pas seulement comment vous pouvez le forcer au-dessus de toute hiérarchie, qu'elle soit bénéfique ou non.

Les flux (ou les périphériques comme dans l'exemple de ChrisW) ont l'avantage de donner un sens à sens . Vous voulez pouvoir traiter tous les flux de la même manière, qu'ils soient connectés à une mémoire tampon, à un fichier ou à un socket réseau. Et tous les périphériques matériels ont de nombreux comportements en commun qui pourraient vraisemblablement être pris en compte dans une classe de base de périphérique.

17
jalf

Beaucoup de gens utilisent l'exemple de Shapes, mais c'est en fait un dangereux. Le problème se pose lorsque vous décidez intuitivement qu’un carré est une sous-classe de rectangle. 

En matière de comportement, un carré est plus limité qu'un rectangle, cassant la substituabilité. Par exemple, nous pourrions demander à un objet rectangle de changer sa hauteur. Si un carré est une sous-classe de rectangle, cela signifie que nous devrions pouvoir demander la même chose d'un carré. Cependant, changer la hauteur d'un carré signifierait que ce n'est plus un carré! Bien sûr, nous pourrions augmenter la largeur en conséquence, mais ce n’est pas ce à quoi nous nous attendions lorsque nous devions demander à un objet de type déclaré rectangle, qui est en fait un carré situé en dessous, de modifier sa hauteur.

C'est ce qu'on appelle le principe de substitution Liskov , et vous devriez en être conscient lors de tout développement sérieux OO. 

Les carrés sont, bien sûr, un sous ensemble de rectangles, au lieu d'un sous classe. C'est la différence entre les approches orientées données et les approches comportementales.

Comme Jon, je préfère Streams à titre d'exemple. Ce n'est pas difficile à expliquer, même aux non-programmeurs, et à son comportement clairement orienté, en évitant la contre-intuitivité de l'exemple de formes.

8
Rik

Si vous aimez les jeux vidéo, cela pourrait ressembler à ceci:

class enemy{
  Health
  Posx
  posy
  Etc
}

class orc : extends enemy{
 speed
 Strength
 etc
}
8
luis

Qu'en est-il d'une hiérarchie d'expressions algébriques. C'est un excellent exemple car il utilise à la fois l'héritage et la composition:

public interface Expression {
    int evaluate();

    public class Constant implements Expression {

        private final int value;

        public Constant(int value) {
            this.value = value;
        }

        @Override
        public int evaluate() {
            return this.value;
        }

        @Override
        public String toString() {
            return String.format(" %d ", this.value);
        }

    }

    public class Negate implements Expression {

        private final Expression expression;

        public Negate(Expression expression) {
            this.expression = expression;
        }

        @Override
        public int evaluate() {
            return -(this.expression.evaluate());
        }

        @Override
        public String toString() {
            return String.format(" -%s ", this.expression);
        }
    }

    public class Exponent implements Expression {

        private final Expression expression;
        private final int exponent;

        public Exponent(Expression expression, int exponent) {
            this.expression = expression;
            this.exponent = exponent;
        }

        @Override
        public int evaluate() {
            return (int) Math.pow(this.expression.evaluate(), this.exponent);
        }

        @Override
        public String toString() {
            return String.format(" %s ^ %d", this.expression, this.exponent);
        }

    }

    public class Addition implements Expression {

        private final Expression left;
        private final Expression right;

        public Addition(Expression left, Expression right) {
            this.left = left;
            this.right = right;
        }

        @Override
        public int evaluate() {
            return this.left.evaluate() + this.right.evaluate();
        }

        @Override
        public String toString() {
            return String.format(" (%s + %s) ", this.left, this.right);
        }
    }

    public class Multiplication implements Expression {

        private final Expression left;
        private final Expression right;

        public Multiplication(Expression left, Expression right) {
            this.left = left;
            this.right = right;
        }

        @Override
        public int evaluate() {
            return this.left.evaluate() *  this.right.evaluate();
        }

        @Override
        public String toString() {
            return String.format(" (%s * %s) ", this.left, this.right);
        }
    }

}

Ensuite, vous pouvez fournir un exemple motivant tel que:

public static void main(String[] args) {

    Expression two = new Constant(2);
    Expression four = new Constant(4);
    Expression negOne = new Negate(new Constant(1));
    Expression sumTwoFour = new Addition(two, four);
    Expression mult = new Multiplication(sumTwoFour, negOne);
    Expression exp = new Exponent(mult, 2);
    Expression res = new Addition(exp, new Constant(1));

    System.out.println(res + " = " + res.evaluate());

}

Ce qui donnerait:

(  ( ( 2  +  4 )  *  - 1  )  ^ 2 +  1 )  = 37
6
Edwin Dalorzo

Je pense que Shape est une bonne classe abstraite. Il existe des formes 2D et 3D. Les formes 2D ont généralement une surface tandis que les formes 3D ont un volume. Les deux peuvent avoir un "lieu" ou un "centre de masse".

Quelques suggestions:

class Shape {..}

class Shape2D extends Shape {...}

class Circle extends Shape2D {...}

class Rectangle extends Shape2D {...}

class Polygon extends Shape2D {...}

class Shape3D extends Shape {...}

class Sphere extends Shape3D {...}
5
SteinNorheim

Etant un admirateur du jeu de contre-grève, voici ce que j'aimerais partager:

enter image description here

4
Yasser

Je suggère des «appareils». Personne ne modélise vraiment les animaux à l'aide de logiciels, mais ils modélisent les appareils.

class Device
{
  void start();
  void stop();
  DeviceStatus status { get; }
}

class VideoDevice : Device
{
  ... methods for any/all video devices ...
}

class DiskDevice : Device
{
  ... methods for any/all disk devices ...
}
3
ChrisW

J'ai toujours aimé:

class Shape {
}
class Square extends Shape {
}

Mais l’un des trois premiers que vous citez ira bien. MountainBike semble le plus excitant. Vous pouvez bien sûr faire la même chose avec les voitures.

2
Iain M Norman

J'aime l'idée d'utiliser des imprimantes comme exemple. Supposons que vous travailliez pour HP, Brother, Lexmark ou une autre entité et que vous deviez concevoir un programme capable de générer des modules spécifiques au pilote pour différents systèmes d'exploitation.

class Printer {
    private String brand;
    private String model;

    public void powerOn() {
        // Logic to power on
    }

    public void powerOff() {
        // Logic to power off
    }

    public void init() {
        // Bootstrap/prep the system
        // Check cartridge
        // Check paper
        // Ready for usage
}

class InkJetPrinter extends Printer {
    // Inherits fields and methods from Printer
    // Demonstrate method overriding and overloading
    // Add new InkJet specific behaviors, components etc.
}

class LaserPrinter extends Printer {
    // Inherits fields and methods from Printer
    // Demonstrate method overriding and overloading
    // Add new Laser specific behaviors, components etc.
}

class LabelPrinter extends Printer {
    // Inherits fields and methods from Printer
    // Demonstrate method overriding and overloading
    // Add new Label specific behaviors, components etc.
}

Après avoir démontré l'héritage, vous pouvez passer à l'abstraction et à la méthode surcharge/override . La méthode init de la classe Printer est un bon candidat pour l'abstraction. Les sous-classes devront implémenter leurs propres processus d'initialisation.

Vous pouvez également étendre cet exemple et commencer à démontrer l'utilisation correcte des interfaces et de la composition. Quelle est la différence entre un concept IS-A et HAS-A? Certaines imprimantes peuvent avoir des capacités WiFi et Bluetooth. Ou, certaines imprimantes InkJet peuvent avoir des capacités de numérisation alors que d'autres ne disposent que de chargeurs, etc. Comment voulez-vous le mettre en œuvre?

Je pense que l’utilisation d’imprimantes est plus étroitement liée aux ordinateurs et à la science informatique en général. Vous pouvez utiliser cet exemple et même en démontrer d'autres exemples traitant de la mise en réseau de systèmes intégrés et de PC, de téléphones intelligents et d'autres IoT.

1
Emir Memic

Webshop pourrait également être une bonne option.

Product(id, title, price, description)
Book(isbn, publisher, pageNr) extends Product
CD(company, duration, genre, taskList) extends Product
1
Victor

J'avais l'habitude de montrer des pièces d'échecs. La classe de base est l'héritage général de ChessPiece, King, Rook, Bishop, etc. 

Ce que j'aime dans cet exemple:

  • c'est réel, si vous implémentez les échecs, c'est une solution
  • en C++, vous pouvez indiquer quelle fonction est virtuelle et quelle n'est pas
  • classe abstraite peut être montré
  • des exemples avancés peuvent évoluer vers une classe de base commune pour les éléments directionnels (Rook et Bishop) ou même davantage vers des éléments directionnels généralisés avec une limite de pas (tout sauf Pawn)
1
Gergely Feldhoffer

J'aime l'exemple des véhicules, car cela permet une extension relativement propre pour inclure des interfaces dans la discussion (IAutomaticGearbox, ça vous tente?)

0
Rowland Shaw

Qu'en est-il de 

class Weapon 
{
}

class Gun : extends Weapon
{
}

class Knife : extends Weapon
{
}

et.

0
Muad'Dib

L'héritage peut être complexe. Commençons par le plus simple de tous: l'héritage comportemental. Ici, vous n'héritez que d'un comportement et d'aucun état. E.g: La personne et l'animal sont des objets Animate qui présentent un comportement IsAlive. Pour utiliser votre exemple:

class LivingThing {
   /* We propose a new type */
  public:
    virtual bool IsAlive() = 0;
    virtual void Birth() = 0;
    virtual void Death() = 0;
};

class Person : public  LivingThing {
    /* A real living thing */
  public:
    virtual bool IsAlive() { return true; }
    virtual void Birth() {}
    virtual void Death() {}
    /* .. and has some special behavior */
    void Marry();
    void Divorce();
};

class Animal: public  LivingThing {
    /* A real living thing */
  public:
    virtual bool IsAlive() { return true; }
    virtual void Birth() {}
    virtual void Death() {}
    /* .. and has some special behavior */
    void Bite();
    void Bark();
};

J'ai écrit ceci en utilisant la syntaxe C++, si vous avez du mal à comprendre quoi que ce soit, dites-le!

0
dirkgently

Le meilleur exemple que j'ai rencontré (et lu dans de nombreux livres) est celui qui utilise Shape.

La meilleure chose à propos de cela est que vous pouvez très facilement expliquer tous les concepts (y compris les plus difficiles) liés aux POO tels que Classe, Objet, Héritage, Abstraction, Encapsulation, Polymorphisme, etc. à tout programmeur indépendant de son expérience.

0
Santosh Gokak

Exemple:

L'approche "Tout dérive de l'objet".

Chien -> Animal -> Chose vivante -> Objet

Un chien est un animal, qui est une chose vivante, qui à son tour est un objet.

0
Elroy

Vous pouvez trouver de bons exemples d'héritage de classe dans les modèles de conception. 

  1. Abstract_factory_pattern : Fournit un moyen d'encapsuler un groupe d'usines individuelles ayant un thème commun sans spécifier leur classe concrète

  2. Template_method_pattern : Il s'agit d'un modèle de conception comportemental qui définit le squelette de programme d'un algorithme dans une opération, reportant certaines étapes aux sous-classes.

  3. Decorator_pattern : Il s'agit d'un modèle de conception qui permet d'ajouter un comportement à un objet individuel, de manière statique ou dynamique, sans affecter le comportement des autres objets de la même classe.

Reportez-vous aux messages ci-dessous pour des exemples concrets:

Quand utiliser le motif de décorateur?

Modèle de conception de modèle dans JDK, impossible de trouver une méthode définissant un ensemble de méthodes à exécuter dans l'ordre

0
Ravindra babu