1. Pégase

Approfondissons le troisième principe de la POO : l'héritage . C'est un sujet très intéressant que vous utiliserez souvent. Pour les non-initiés, la programmation est indiscernable de la magie. Alors commençons par une analogie intéressante...;

Disons que vous êtes un sorcier qui veut créer un cheval volant. D'une part, vous pourriez essayer de conjurer un pégase. Mais comme les pégases n'existent pas dans la nature, ce sera très difficile. Vous devrez faire beaucoup vous-même. Il est beaucoup plus facile de prendre un cheval et de conjurer ses ailes.

En programmation, ce processus est appelé "héritage". Supposons que vous ayez besoin d'écrire une classe très complexe. Il faut beaucoup de temps pour écrire du code à partir de zéro, puis tout tester pendant longtemps pour rechercher les erreurs. Pourquoi aller à la dure ? Il vaut mieux regarder pour voir si une telle classe existe déjà.

Supposons que vous trouviez une classe dont les méthodes implémentent 80 % des fonctionnalités dont vous avez besoin. Qu'en faites-vous ensuite ? Vous pouvez simplement copier son code dans votre classe. Mais cette solution présente plusieurs inconvénients :

  1. La classe que vous trouvez peut déjà être compilée en bytecode et vous n'avez peut-être pas accès à son code source.
  2. Le code source de la classe est disponible, mais vous travaillez pour une entreprise qui pourrait être poursuivie pour quelques milliards pour avoir utilisé ne serait-ce que 6 lignes du code de quelqu'un d'autre. Et puis votre employeur vous poursuivra.
  3. Duplication inutile d'une grande quantité de code. De plus, si l'auteur d'une classe externe y trouve un bogue et le corrige, vous aurez toujours le bogue.

Il existe une solution plus élégante, et elle ne nécessite pas d'avoir un accès légal au code de la classe d'origine. En Java, vous pouvez simplement déclarer cette classe comme parent de votre classe. Cela équivaudra à ajouter le code de cette classe à votre propre code. Votre classe verra toutes les données et toutes les méthodes de la classe parent. Par exemple, vous pouvez faire ceci : on hérite de "cheval" puis on ajoute des "ailes" pour obtenir un "pégase"


2. Classe de base commune

L'héritage peut également être utilisé à d'autres fins. Disons que vous avez dix classes très similaires. Ils ont les mêmes données et méthodes. Vous pouvez créer une classe de base spéciale, déplacer les données (et les méthodes associées) dans cette classe de base et déclarer ces dix classes comme descendantes. En d'autres termes, dans chaque classe indique que sa classe mère est cette classe de base.

Tout comme les avantages de l'abstraction ne se révèlent que parallèlement à l'encapsulation, les avantages de l'héritage le sont également beaucoup plus lors de l'utilisation du polymorphisme. Mais vous le saurez un peu plus tard. Aujourd'hui, nous allons examiner plusieurs exemples d'utilisation de l'héritage.

Pièces d'échec

Supposons que nous écrivions un programme qui joue aux échecs avec un utilisateur humain. En conséquence, nous avons besoin de classes pour représenter les pièces. De quelles classes seraient-ils ?

Si vous avez déjà joué aux échecs, la réponse évidente est le roi, la reine, le fou, le cavalier, la tour et le pion.

Mais les classes elles-mêmes auraient toujours besoin de stocker des informations sur chaque pièce. Par exemple, les coordonnées x et y, et la valeur de la pièce. Après tout, certaines pièces ont plus de valeur que d'autres.

De plus, les pièces se déplacent différemment, ce qui signifie que les classes mettront en œuvre un comportement différent. Voici comment vous pourriez les définir en tant que classes :

class King
{
   int x;
   int y;
   int worth;

   void kingMove()
   {
     // Code that decides
     // how to move
     // the king
   }
}
class Queen
{
   int x;
   int y;
   int worth;

   void queenMove()
   {
     // Code that decides
     // how to move
     // the queen
   }
}
class Rook
{
   int x;
   int y;
   int worth;

   void rookMove()
   {
     // Code that decides
     // how to move
     // the rook
   }
}
class Knight
{
   int x;
   int y;
   int worth;

   void knightMove()
   {
     // Code that decides
     // how to move
     // the knight
   }
}
class Bishop
{
   int x;
   int y;
   int worth;

   void bishopMove()
   {
     // Code that decides
     // how to move
     // the bishop
   }
}
class Pawn
{
   int x;
   int y;
   int worth;

   void pawnMove()
   {
     // Code that decides
     // how to move
     // the pawn
   }
}

C'est une description très primitive des pièces d'échecs.

Classe de base commune

Et voici comment vous pouvez utiliser l'héritage pour réduire la quantité de code. Nous pouvons regrouper les méthodes et les données communes dans une classe commune. Nous l'appellerons ChessItem. Il ne sert à rien de créer des objets de la ChessItem class, puisque la classe ne correspond à aucune pièce d'échecs . Cela dit, la classe s'avérera très utile :

class King extends ChessItem
{
   void kingMove()
   {
     // Code that decides
     // how to move the king
   }
}
class Queen extends ChessItem
{
   void queenMove()
   {
     // Code that decides
     // how to move the queen
   }
}
class Rook extends ChessItem
{
   void rookMove()
   {
     // Code that decides
     // how to move the rook
   }
}
class ChessItem
{
   int x;
   int y;
   int worth;
}
class Knight extends ChessItem
{
   void knightMove()
   {
     // Code that decides
     // how to move the knight
   }
}
class Bishop extends ChessItem
{
   void bishopMove()
   {
     // Code that decides
     // how to move the bishop
   }
}
class Pawn extends ChessItem
{
   void pawnMove()
   {
     // Code that decides
     // how to move the pawn
   }
}

C'est un excellent moyen de simplifier le code pour des objets similaires. Les avantages sont particulièrement visibles lorsqu'il y a des milliers d'objets différents et des centaines de classes dans le projet. Ainsi, les classes parentes (de base) correctement sélectionnées vous permettent non seulement de simplifier considérablement la logique, mais également de décupler le code.


3. Héritage de classe —extends

Alors que faut-il pour hériter d'une classe ? Pour qu'une classe hérite d'une autre, vous devez écrire le extendsmot-clé après la déclaration de la classe enfant, puis écrire le nom de la classe parent. Cela ressemble généralement à ceci :

class Descendant extends Parent

C'est ce que vous devez écrire lors de la déclaration de la classe Descendant. Au fait, une classe ne peut hériter que d'une seule classe.

Sur la photo, on voit qu'une vache a hérité d'un cochon, qui a hérité d'une poule, qui a hérité d'un œuf. Un seul parent ! Un tel héritage n'est pas toujours logique. Mais quand tout ce que vous avez est un cochon et que vous avez vraiment besoin d'une vache, les programmeurs ne peuvent souvent pas résister à l'envie de faire une vache d'un cochon.

Java n'a pas d'héritage multiple : une classe ne peut pas hériter de deux classes. Chaque classe ne peut avoir qu'une seule classe mère. Si aucune classe parent n'est spécifiée, la classe parent est Object.

Cela dit, Java possède plusieurs héritages d'interfaces. Cela atténue légèrement le problème. Nous parlerons des interfaces un peu plus tard, mais pour l'instant continuons à explorer l'héritage.