Dans le chapitre précédent, nous avons utilisé une énumération pour modifier l'initialisation de la classe RoadSection
, ce qui nous a permis d'écrire ceci :
RoadSection(type: .plain)
RoadSection(type: .home)
RoadSection(type: .school)
C'est déjà bien, mais ce n'est pas exactement l'objectif souhaité. À l'origine, nous souhaitions utiliser l'héritage pour initialiser différents types de sections de route. On aimerait plutôt pouvoir écrire ceci :
RoadSection()
HomeRoadSection()
SchoolRoadSection()
Alors, allons-y ! Ça va être super !
Utilisez super
Je vous propose de commencer tout simplement par déclarer nos classes HomeRoadSection
et SchoolRoadSection
, en les faisant hériter de RoadSection
:
class HomeRoadSection: RoadSection {
}
class SchoolRoadSection: RoadSection {
}
C'est un bon début. Maintenant nous allons les initialiser en dessinant sur le canevas la section de route correspondante :
class HomeRoadSection: RoadSection {
init() {
}
}
class SchoolRoadSection: RoadSection {
init() {
}
}
Et là, réfléchissons un peu. Nous avons déjà fait le travail dans l'initialisation de RoadSection
pour que la bonne section de canevas soit dessinée en fonction de son type. Est-ce qu'il n'y aurait pas un moyen de réutiliser l'initialisation de RoadSection
dans ses classes filles ?
Vous vous en doutez, il y a moyen. Et le moyen, c'est super
! Oui, le mot super
. Le mot super
dans une classe permet de faire référence à la classe mère de la classe dans laquelle on se trouve. C'est un peu l'équivalent du mot self
. Sauf que self
fait référence à soi-même, alors que super
fait référence à la classe mère.
Donc ici, nous allons utiliser super
pour récupérer l'initialisation de RoadSection
:
class HomeRoadSection: RoadSection {
init() {
super.init(type: .home)
}
}
class SchoolRoadSection: RoadSection {
init() {
super.init(type: .school)
}
}
Maintenant, voyons ce qu'il se passe si j'écris ceci :
HomeRoadSection()
Je vais tout d'abord appeler l'initialisation de HomeRoadSection
. Ensuite, je vais, par le mot clé super
, appeler l'initialisation de RoadSection
en lui passant le paramètre .home
. Cela nous fait passer dans un switch qui appelle la méthode : canvas.createHomeRoadSection()
.
Maintenant, je vous propose de rajouter la propriété children
à la classe HomeRoadSection
, qui va nous permettre de savoir combien d'enfants habitent la maison. J'en profite en même temps pour l'inclure dans notre initialisation.
class HomeRoadSection: RoadSection {
var children: Int
init(children: Int) {
self.children = children
super.init(type: .home)
}
}
Utilisez override
Chauffe Marcel !
Nos sections de route sont maintenant très pratiques, mais notre bus scolaire a encore triste mine :
class SchoolBus: Bus {
var schoolName = ""
}
Pourtant nous avions de grands projets pour lui, souvenez-vous :
Nous voulions lui ajouter une fonction drive
.
Mais SchoolBus
hérite de Bus
?!
Oui... Et ?
Du coup, il a déjà à disposition la fonction drive
qui est définie dans Bus
!
Eh oui ! Bien vu ! Mais la fonction drive
de Bus
ne fait que conduire le long de la route sans s'arrêter. Alors que le bus scolaire, lui, doit s'arrêter à chaque maison, récupérer les enfants et ensuite s'arrêter à l'école.
On n’a qu'à changer la méthode drive directement dans Bus
.
Oui, mais ce serait un peu faux. Un bus normal ne s'arrête pas devant les maisons et ne va pas à l'école.
Alors qu'est-ce qu'on fait ?
Eh bien on utilise l'override !
L'overquoi ?
L'override est une technique qui permet à une classe fille de réécrire une méthode de la classe mère.
Imaginons que la méthode drive
soit un DVD sur lequel se trouve l'épisode 4 de Star Wars. Il existe deux façons de faire l'override :
soit on grave sur le DVD l'épisode 3 (avant) et/ou l'épisode 5 (après) en plus de l'épisode 4 ;
soit on efface tout et on met Bambi.
Autrement dit, l'override permet à une classe fille de compléter l'implémentation d'une méthode la classe mère ou de la réécrire complètement. Dans les deux cas, comment fait-on ?
On utilise tout simplement le mot-clé override
. Regardons ça avec un exemple.
Prenons la classe Animal
suivante :
class Animal {
func sayHello() {
print("Bonjour")
}
func describeYourself() {
print("Je suis un animal.")
}
}
Maintenant prenons une classe fille Dog
:
class Dog: Animal {
}
Pour redéfinir les méthodes, il me faut rajouter le mot-clé override
avant ma fonction :
class Dog: Animal {
override func sayHello() {
print("Wouf !")
}
override func describeYourself() {
super.describeYourself()
print("Et pas n'importe lequel : un chien !")
}
}
Voyons un peu ce que j'ai fait ici. J'ai modifié la méthode sayHello
. Désormais elle n'affiche plus "Bonjour", mais "Wouf !". La méthode a été entièrement modifiée, on ne réutilise rien de la méthode originale. En revanche, dans la méthode describeYourself
, j'appelle la méthode describeYourself
de la classe Animal
en utilisant le mot clé super
, comme pour l'initialisation de tout à l'heure. Ensuite, je complète la méthode en ajoutant "Et pas n'importe lequel : un chien !". Du coup, si j'écris :
let dog = Dog()
dog.describeYourself()
… la console affiche :
Je suis un animal. Et pas n'importe lequel : un chien !
À vous de jouer !
Ce que nous avons appris dans ce chapitre va vous permettre d'améliorer notre programme !
Exercice 1
En vous inspirant de la fonction createStraightRoad
, créez une fonction createRoadToSchool
qui permet de créer une route ayant les caractéristiques suivantes :
une longueur de route de 30 ;
toutes les 7 sections, une section contient une maison ;
la dernière section contient une école. Pour cet exercice, vous utiliserez les classes
HomeRoadSection
etSchoolRoadSection
que nous avons ajoutées dans ce chapitre.
Exercice 2
En utilisant la technique de l'override, vous implémenterez la méthode drive
de la classe SchoolBus
. Le bus doit marquer un arrêt à toutes les maisons et à l'école.
Exercice 3
Le contenu de l’exercice se trouve dans le dossier Github P3C3.
Ouvrez un nouveau Playground Xcode.
Copiez le contenu du fichier “main.swift” dans votre Playground.
Suivez les instructions.
En résumé
Le mot-clé
super
permet d'accéder dans une classe fille à l'implémentation d'une méthode ou d'une initialisation de la classe mère.Le mot-clé
override
permet de modifier dans une classe fille une méthode définie dans une classe mère soit en la complétant, soit en la redéfinissant complètement.
Vous avez appris dans ce chapitre les subtilités de l’héritage allié aux méthodes, maintenant regardons ensemble comment mieux gérer les types que vous avez créés avec l’héritage !