Java est le premier langage que j'ai appris dans ma carrière. Sa structure est fondamentale dans mes premières années de compréhension des concepts de programmation. Après avoir parcouru plusieurs autres langages avec des approches très différentes, j'ai élargi mon point de vue. Aujourd'hui, je souhaite réfléchir à l'idée d' héritage .
En Java, l'idée d' héritage est étroitement liée au concept de sous-typage . Le sous-typage est l'implémentation d'une relation IS A. Par exemple, la classe Rabbit
est un sous-type de la classe Mammal
. Dès lors, une instance Rabbit
a tout le comportement d'un Mammal
: elle hérite du comportement.
De ce fait, vous pouvez transmettre une instance Rabbit
lorsqu'une méthode appelle un paramètre Mammal
ou renvoyer une instance Rabbit
lorsqu'un type de retour de méthode est Mammal
. Si vous avez appris Java, .Net ou quelque chose de vaguement similaire, c'est ainsi que vous voyez l'héritage, et cela devient la nouvelle norme.
Il s'agit d'un héritage explicite .
class Animal { void feed(); } class Rabbit extends Animal { //1 }
Rabbit
IS A
Animal
, il peut feed()
Quand j'ai découvert Go pour la première fois, j'ai été étonné de constater qu'il ne possède pas de sous-typage tout en offrant un héritage. Go utilise le typage canard :
Si cela ressemble à un canard, nage comme un canard et cancane comme un canard, alors c'est probablement le cas.
Si une struct
Go implémente les mêmes fonctions qu'une interface, elle implémente implicitement l'interface.
type Animal interface { feed() //1 } type Rabbit struct { } func (rabbit Rabbit) feed() { //2 // feeds }
Animal
peut se nourrirfeed()
qui prend un Rabbit
comme paramètre, Rabbit
implémente Animal
Je n'aime pas Go pour son approche de gestion des erreurs, mais j'étais partagé sur l'implémentation implicite. D'un côté, j'ai compris que c'était un nouveau concept et j'ai essayé de rester ouvert d'esprit ; de l'autre, je pense que les choses sont toujours meilleures de manière explicite qu'implicite, que ce soit dans le développement logiciel ou dans la vie réelle.
Python est le langage le plus intéressant que je connaisse en matière d’héritage.
Le sous-typage et l’héritage basé sur les types sont présents dans Python depuis sa création.
class Animal: def feed(self): #1 pass #2 class Rabbit(Animal): #3 pass
Animal
peut se nourrirRabbit
IS A
Animal
, il peut feed()
À cet égard, Python fonctionne de la même manière que Java en termes d'héritage. Python propose également le typage canard, que j'ai décrit comme des méthodes magiques . Par exemple, pour rendre quelque chose d'itérable , par exemple , qui peut renvoyer un itérateur , il vous suffit d'implémenter __iter__()
et __next__()
:
class SingleValueIterable(): done = False def __init__(self, value): self.value = value def __iter__(self): #1 return self def __next__(self): #1 if self.done: raise StopIteration else: self.done = True return self.value svi = SingleValueIterable(5) sviter = iter(svi) #2 for x in sviter: print(x) #3
Iterator
– Python sait comment faire puisque nous avons implémenté les méthodes ci-dessus5
Le problème avec cette approche de typage de canard est qu'elle ne fonctionne que pour les méthodes magiques prédéfinies de Python. Que faire si vous souhaitez proposer une classe dont un tiers pourrait hériter implicitement ?
class Animal: def feed(): pass class Rabbit: def feed(): pass
Dans l'extrait ci-dessus, Rabbit
n'est pas un Animal
, à notre grand regret. C'est là qu'intervient le PEP 544 , intitulé Protocols: Structural subtyping (static duck typing). Le PEP résout l'impossibilité de définir des méthodes magiques pour nos classes. Il définit une classe Protocol
simple : une fois que vous en héritez, les méthodes définies dans la classe deviennent éligibles au typage duck, d'où le nom - typage duck statique.
from typing import Protocol class Animal(Protocol): #1 def feed(): #2 pass class Rabbit: def feed(): #2 pass class VenusFlytrap: def feed(): #2 pass
Protocol
Animal
est un Protocol
, toute classe qui définit feed()
devient un Animal
, pour le meilleur ou pour le pireLa programmation orientée objet, l'héritage et le sous-typage peuvent avoir des significations spécifiques qui ne se traduisent pas dans d'autres langages, en fonction du premier langage que vous apprenez. Java se présente comme un langage orienté objet et offre le package complet. Go n'est pas un langage OO, mais il propose toujours le sous-typage via le typage canard. Python offre à la fois l'héritage explicite et implicite, mais aucune interface.
Vous apprenez un nouveau langage de programmation en le comparant à celui(s) que vous connaissez déjà. Connaître les caractéristiques d'un langage est essentiel pour écrire du code idiomatique dans votre langage cible. Familiarisez-vous avec les fonctionnalités qui n'existent pas dans vos langages connus : elles élargiront votre compréhension de la programmation dans son ensemble.
Aller plus loin :
Initialement publié sur A Java Geek le 26 janvier 2025