• Aucun résultat trouvé

Le polymorphisme d’héritage

Le polymorphisme d’héritage est le deuxième type de polymorphisme.

Il permet de donner à une classe certaines caractéristiques et fonctionnalités d’une classe existante. En quelque sorte, l’héritage permet de spécialiser une classe de base.

La classe spécialisée est dite "dérivée" ou "étendue", ou encore appelée

"classe fille".

La classe de base est dite "étendue", ou encore appelée "classe mère".

Par exemple, prenons une classe de baseAvion. Un exemple d’héritage possible serait une classeAvionDeLigne ou encoreAvionDeChasse, qui posséderaient les caractéristiques d’un avion, mais qui auraient leurs propres spécificités, par exemple une liste de passagers pour l’avion de ligne, et une liste d’équipements militaires pour l’avion de chasse. Nous utiliserons cet exemple pour illustrer ce mécanisme important de la programmation orientée objet.

Pour indiquer qu’une classe dérive d’un autre classe, il suffit d’utiliser le mot-cléInherits, suivi de la classe de base :

Public Class Avion

’ Nous ne remplissons pas le contenu

’ Nous le ferons au fur et à mesure

’de la découverte de l’héritage End Class

Public Class AvionDeChasse Inherits Avion

End Class

De cette manière, vous avez fait de la classe AvionDeChasse une spécialisation de la classe Avion. Contrairement au polymorphisme d’interface, où une classe pouvait implémenter plusieurs interfaces, une

Passer au niveau supérieur Chapitre 9

classe ne peut dériver que d’une seule classe de base. Lorsque les deux types de polymorphismes sont utilisés, il faut d’abord écrire l’héritage :

Public Class AvionDeChasse Inherits Avion

Impliments ISupersonic End Class

Il est possible d’indiquer qu’une classe n’est pas dérivable quand l’héritage n’apporte aucune information supplémentaire pertinente.

Dériver une classe Cloture présente peu d’intérêt : si vous avez des clôtures en bois ou des haies, vous pouvez le préciser dans la classe Cloture par un attribut de type énumération par exemple. Les fonctionnalités d’une clôture ne changent pas pour autant.

Pour empêcher la dérivation, on utilise le mot-cléNotInheritable:

Public NotInheritable Class Cloture

’ Il n’est pas nécessaire de détailler cette classe End Class

Public Class ClotureHaie Inherits Cloture End Class

La classeCloture n’étant pas dérivable,ClotureHaiene pourra pas en hériter. Toute tentative de dérivation provoquera une erreur.

Vous avez vu comment gérer les héritages au niveau des classes, mais cela ne suffit pas. Il faut aussi le faire au niveau des membres.

Figure 9.16: Tentative de dérivation d’une classe non dérivable

La programmation orientée objet Chapitre 9

189 LE GUIDE COMPLET

L’héritage des membres

Le fait de définir des héritages au niveau des classes permet juste de dire qu’une classe est une classe fille d’une autre. Cependant, une classe fille ne va pas forcément tout hériter de la classe mère. Les membres dont va hériter la classe fille dépendent en grande partie du niveau d’accès et de l’encapsulation de ces membres dans la classe mère. Si le membre de la classe mère est privé, il ne sera pas accessible, même à une classe fille.

S’il est public, il est accessible à tout le monde, donc à la classe fille aussi.

Public Class Avion

Private avionDeBase As Boolean Public nbreAilerons As Integer End Class

Public Class AvionDeChasse Inherits Avion

Public Sub afficheMesMembresHerites MessageBox.Show(nbreAilerons) MessageBox.Show(avionDeBase) End Sub

End Class

Cela provoquera une erreur car, l’attributavionDeBaseétantPrivate, il n’est pas accessible dans la classeAvionDeChasse, même si celle-ci dérive de la classeAvion.

Lorsqu’il a été question de l’encapsulation, nous avons parlé du modificateur d’accès Protected. Il permet de limiter l’accès à un membre exclusivement à ses classes filles. C’est typiquement le modificateur d’accès utilisé dans les cas d’héritage. Il permet de conserver l’aspect sécuritaire en évitant un accès public. Grâce à lui, les

Figure 9.17: Tentative d’accès à un membre privé de la classe mère Passer au niveau supérieur

Chapitre 9

classes filles peuvent bénéficier de la fonctionnalité correspondante.

Dans le code précédent, l’attribut nbreAilerons est Public, ce qui est déconseillé.

Public Class Avion

Protected vitesseMax As Integer Protected nbreAilerons As Integer Protected reserveKerozene As Integer Public Sub vitesseMaximum()

Les membres reserveKerozene et nbreAilerons étant maintenant protégés, la classeAvionDeChassepeut les utiliser directement. Il n’y a alors plus d’erreur sur la méthodeafficheMesMembresHerites. La fonction vitesseMaximum pousse l’avion à sa vitesse maximale.

Comme elle est Public, AvionDeChasse en hérite et on peut l’appeler :

Dim monChasseur As New AvionDeChasse monChasseur.vitesseMaximum()

Cela ne pose pas de problème en termes de programmation. Le compilateur ne détecte pas d’erreur. À l’exécution, l’attribut vitesseMax a une valeur de 1 000 et l’affiche dans une boîte de dialogue.

C’est au niveau du sens qu’il y a un problème. Bien qu’étant un avion, un chasseur a une vitesse maximale bien supérieure à celle d’un avion de base, d’un avion de ligne ou d’un planeur. De plus, pour pouvoir atteindre celle-ci, l’avion de chasse doit activer le mécanisme de post-combustion. C’est pourquoi l’implémentation de la vitesse maximale dans la classe de base ne convient plus du tout pour la classe dérivée. Il faut donc la remplacer.

La programmation orientée objet Chapitre 9

191 LE GUIDE COMPLET

Pour cela, il faut d’abord spécifier dans la classe de base que ce membre est redéfinissable. On utilise en ce sens le mot-cléOverridable. Puis dans la classe dérivée, il faut redéfinir et réimplémenter cette méthode. Il faut préciser que la nouvelle méthode est une redéfinition de celle qui existait dans la classe de base. Cela se fait grâce au mot-cléOverrides.

Public Class Avion

Protected vitesseMax As Integer Protected nbreAilerons As Integer Protected reserveKerozene As Integer

’ On définit cette méthode comme Overridable

’ On pourra alors la redéfinir dans les classes filles Public Overridable Sub vitesseMaximum()

Me.vitesseMax = 1000

’ Activation de la post-combustion Public Sub postCombustion()

’ L’implémentation n’est pas nécessaire

’ pour la compréhension End Sub

’ Redéfinition de la méthode vitesseMaximum

’ Qui est une méthode Overridable de la classe de base Public Overrides Sub vitesseMaximum()

Me.postCombustion()

Cela vous permet de redéfinir un comportement dans la mesure où celui-ci est équivalent, c’est-à-dire si la méthode en question a la même signature. Si par exemple, il fallait préciser un paramètre pour la post-combustion, il ne serait plus possible de redéfinir ainsi la méthode vitesseMaximum.

Passer au niveau supérieur Chapitre 9

Dans ce cas, il est préférable de masquer la méthode de la classe de base. De cette manière, on peut écrire une méthode qui a le même nom, mais dont la signature est différente. Ce principe se rapproche un peu de la surcharge, mais il intervient entre des classes mères et des classes filles, et non pas sur une unique classe. Du côté de la classe mère, rien de ne change. On ne peut masquer que des méthodes Overridable. Cependant, au niveau de la classe fille, on utilisera le mot-cléShadows, puis l’on pourra redéfinir normalement la méthode, avec éventuellement une nouvelle signature, une valeur de retour différente, etc.

Public Class Avion

Protected vitesseMax As Integer Protected nbreAilerons As Integer Protected reserveKerozene As Integer

’ La méthode vitesseMaximum, redéfinissable Public Overridable Sub vitesseMaximum()

Me.vitesseMax = 1000

’ Activation de la post-combustion

Public Sub postCombustion(ByVal force As Integer)

’ L’implémentation n’est pas

’ nécessaire pour la compréhension End Sub

’ Masquage de la méthode vitesseMaximum

’ La signature est différente de celle de la classe mère

’ Il y a un paramètre

Public Shadows Sub vitesseMaximum(ByVal force As Integer) Me.postCombustion(force)

Bien que cela vous permette de vous adapter au fonctionnement des différentes classes, il y a un risque de problème :

Dim monAvion As New Avion()

Dim monChasseur As New AvionDeChasse() monAvion.vitesseMaximum()

monChasseur.vitesseMaximum()

La programmation orientée objet Chapitre 9

193 LE GUIDE COMPLET

Cela provoquera une erreur, car la fonction de base étant masquée par la nouvelle,vitesseMaximumsans paramètre n’existe pas pour la classe AvionDeChasse.

Il faut donc faire attention lorsque vous utilisez des méthodes masquées, car ce qui était valable à un moment ne l’est plus du tout à un autre.

Le fait de masquer ou de redéfinir une méthode redéfinissable vous donne accès à deux implémentations de cette méthode. Celle qui est utilisée est généralement celle de la classe fille, la plus basse dans la hiérarchie. Cependant, vous pouvez accéder à l’implémentation de la classe mère. Le mot-clé MyBase est une référence à la classe mère et permet d’appeler les membres de la classe de base :

Public Class AvionDeChasse Inherits Avion

[…]

Public Sub vitesseMaximumMere() MyBase.vitesseMaximum() End Sub

End Class

La méthode vitesseMaximumMere exécutera la méthode vitesseMaximum dans la classe de base, c’est-à-dire qu’elle définira l’attributvitesseMaxà 1 000 et l’affichera dans une boîte de dialogue.

L’abstraction

Selon les composants que vous avez à décrire et que vous devez représenter, vous pouvez avoir besoin d’une classe de base qui décrit

Figure 9.18: Tentative d’accès à une fonction masquée Passer au niveau supérieur Chapitre 9

une multitude de caractéristiques communes, sans qu’un objet de cette classe puisse exister. Cela veut dire qu’une spécialisation est obligatoire.

Prenons les avions par exemple. La classe de base décrirait qu’un avion a des ailes, une vitesse maximale, un poids, une longueur, etc.

Cependant, on peut considérer le concept d’avion trop général et avoir envie de forcer l’utilisation d’une spécialisation pour définir les avions de chasse, les planeurs, les avions de ligne.

Ce mécanisme est l’abstraction. Il empêche la création d’objets d’une classe de base et force donc la dérivation. L’objet dérivé est considéré comme une fille, avec toutes les possibilités vues précédemment.

Pour qu’une classe soit abstraite, il faut utiliser le mot-clé MustInherit:

Public MustInherit Avion

’ L’implémentation n’est pas nécessaire pour la

compréhension End Class

Vous ne pouvez pas créer un avion de base car la classe est abstraite, sous peine d’obtenir une erreur.

L’abstraction de la classe vous force donc à spécialiser cette classe. Une fois cela fait, la classe fille aura accès aux membres de la classe de base, comme dans un héritage classique.

Dans une classe abstraite, il est possible de définir des membres abstraits. Cela se rapproche des interfaces. Dans les interfaces, il faut que la classe implémente l’ensemble des membres de l’interface. Pour les membres abstraits, le principe est le même. Pour qu’une classe

Figure 9.19: Tentative d’instanciation d’une classe abstraite

La programmation orientée objet Chapitre 9

195 LE GUIDE COMPLET

puisse dériver d’une classe abstraite qui possède des membres abstraits, elle doit implémenter chacun de ces membres. Dans la classe abstraite, cela se précise par le mot-cléMustOverride:

Public MustInherit Avion

’ L’implémentation n’est pas nécessaire pour la

compréhension

Public MustOverride vitesseMaximum() End Class

Pour pouvoir hériter de la classe Avion, une classe devra fournir une implémentation devitesseMaximum:

Public Class AvionDeChasse Inherits Avion

’ Activation de la post-combustion

Public Sub postCombustion(ByVal force As Integer)

’ L’implémentation n’est pas nécessaire pour la

compréhension End Sub

’ On omet de redéfinir la méthode vitesseMaximum End Class

Comme ce n’est pas le cas ici, l’héritage ne pourra se faire et vous obtiendrez une erreur.

Comme pour les membres d’une interface, l’ensemble des membres abstraits doivent avoir une implémentation dans la classe fille :

Public Class AvionDeChasse Inherits Avion

’ Activation de la post-combustion Public Sub postCombustion()

’L’implémentation n’est pas nécessaire

’pour la compréhension End Sub

Figure 9.20: Instanciation d’une classe fille sans redéfinir les membres abstraits

Passer au niveau supérieur Chapitre 9

’Implémentation de la méthode abstraite

’de la classe Avion

Public Override Sub vitesseMaximum() Me.postCombustion()

Maintenant AvionDeChasse peut dériver de la classe Avion, qui est abstraite. De cette manière, AvionDeChasse bénéficie de toutes les caractéristiques de la classe Avion. Mais le fait que celle-ci soit abstraite garantit qu’il ne peut y avoir que des objets complets en termes d’informations et de fonctionnalités.

Vous venez de voir les grandes principes de la programmation orientée objet. Spécialement en Visual Basic .NET, la programmation orientée objet (POO, à prononcer "pou") est la base de tout. C’est pourquoi il est important de bien la comprendre et de bien la maîtriser. Le tout est de s’entraîner et de pratiquer.

9.2. La vie des données

En programmation, tous les traitements et la gestion des données sont pris en charge par des mécanismes qui travaillent sur la mémoire en arrière-plan. Les variables correspondent à des emplacements en mémoire avec des valeurs particulières. De la même manière, les objets sont des morceaux de mémoire d’une taille donnée où sont stockées les valeurs nécessaires.

C’est pourquoi la compréhension du fonctionnement de la mémoire permet de mieux comprendre les comportements intervenant dans un programme. Dans cette section, nous allons donner une explication détaillée de ces mécanismes. Vous verrez comment cela se passe au niveau des variables locales, mais aussi des objets, de leurs constructeurs et destructeurs, et comment est récupérée la mémoire vacante grâce au principe du ramasse-miettes (garbage collector).

La vie des données Chapitre 9

197 LE GUIDE COMPLET