C'est parti ! Nous allons parler des protocoles ! Si vous suivez le parcours, on en a déjà un peu parlé. Mais cette fois-ci, on va vraiment rentrer dans le détail !
Une histoire d'héritage...
Pour découvrir les protocoles, je vous ai concocté un petit Playground !
Dans ce Playground, vous trouverez un code assez simple : une classe Animal
et deux sous-classes Bird
et Dog
.
La classe Animal
a deux méthodes et une propriété, comme suit :
class Animal {
func makeSound() {}
func move() {}
var description = ""
}
Mais les méthodes sont vides ! Elles ne servent à rien ?
Très bonne question ! En effet, les méthodes sont vides, car je ne peux pas prédire le son que va faire un animal, sans savoir précisément de quel animal je parle. De même, un oiseau ne se déplace pas de la même manière qu'un cheval. Donc je ne peux pas remplir la méthode move
.
Autrement dit, la classe Animal
est trop générique. De ce fait, elle ne sert à rien, à part mettre en commun deux méthodes et une propriété que les sous-classes vont pouvoir implémenter.
En programmation, on n’aime pas trop ce qui ne sert à rien ! En biologie non plus, d'ailleurs... mais c'est un autre débat ! On va donc supprimer cette classe Animal
!
Mais attends ! Elle permet quand même aux sous-classes d'hériter de ces méthodes et de cette propriété !
Je sais, mais faites-moi confiance, supprimez cette classe ! On va faire cela différemment... Avec un protocole !
Les protocoles à la rescousse !
En plus de beaucoup d'autres choses, les protocoles sont très pratiques dans notre situation. Et pour comprendre pourquoi, il faut comprendre ce qu'est un protocole !
Définition
Un protocole, c'est une liste d'exigences.
Oui, je sais, dit comme ça, cela ne fait pas rêver... Mais vous allez voir, c'est puissant ! Rentrons dans le vif du sujet !
Syntaxe
Pour déclarer un protocole, on utilise le mot-clé protocol
suivi d'un nom et d'accolades :
protocol Animal {
}
Oui, cela ressemble à une classe. Et la ressemblance ne s'arrête pas là !
Méthodes
On peut ajouter des méthodes à un protocole.
protocol Animal {
func makeSound()
func move()
}
Hé, mais attends ! Tu as oublié l'implémentation des méthodes.
Eh oui, comme je l'ai dit, un protocole est une liste d'exigences. Ensuite, on va demander à une classe de se conformer à ces exigences.
Propriétés
Un protocole peut aussi contenir des propriétés. Mais, comme pour les méthodes, le protocole va seulement définir si cette propriété peut être modifiée ou non. Il ne va pas lui donner de valeur :
protocol Animal {
func makeSound()
func move()
var description: String { get set }
}
Je définis ici dans mon protocole une propriété description de type String
. Jusque-là rien de nouveau. Ensuite, j'ajoute entre accolades les mots get
et set
. Cela signifie que cette propriété pourra être modifiée.
var description: String { get }
Je veux bien toute cette histoire de protocole, mais à quoi cela sert-il vraiment ?
Je comprends que ce soit un peu flou pour le moment, mais on y vient !
Se conformer à un protocole
Je le répète : un protocole, c'est une liste d'exigences. Autrement dit, un protocole, c'est une entité qui déclare :
Si tu veux être ça, il faut que tu fasses ça.
Par exemple, dans notre cas :
Si tu veux être un Animal
, il faut que tu saches faire les choses suivantes :
makeSound
;move
.
Et donner une valeur à la propriété suivante :
description
.
Nous avons dans notre Playground deux classes qui voudraient bien être des animaux : Dog
et Bird
.
class Dog {
var race = ""
func fetch() {
print("Je vais chercher la balle...")
}
}
class Bird {
var color = ""
}
Pour que Dog
et Bird
soient des animaux, ils doivent se conformer au protocole. Cela se fait en deux étapes :
Adopter le protocole.
Répondre à ses exigences.
Commençons par la première étape :
Adopter le protocole
Pour adopter un protocole, la syntaxe est exactement la même que pour l'héritage :
class Dog: Animal { (...) }
class Bird: Animal { (...) }
On ajoute :
puis le nom du protocole ( Animal
) après la déclaration de la classe.
À ce moment-là, vous devriez avoir une erreur dans votre Playground :
Cette erreur signifie simplement que nous avons adopté le protocole, mais que nous n'avons pas encore répondu à ses exigences. C'est normal, c'est notre deuxième étape !
Répondre à ses exigences
Pour répondre aux exigences d'un protocole, il suffit de rajouter l'implémentation des méthodes du protocole, et de donner une valeur à ses propriétés, comme ceci :
class Dog: Animal {
var race = ""
func fetch() {
print("Je vais chercher la balle...")
}
// On répond aux exigences du protocole
var description = ""
func move() {
print("Je cours !")
}
func makeSound() {
print("Wouf !")
}
}
class Bird: Animal {
var color = ""
// On répond aux exigences du protocole
var description = ""
func move() {
print("Je vole !")
}
func makeSound() {
print("Piou Piou")
}
}
On a simplement rajouté les méthodes et leurs implémentations, et on a donné une valeur à la propriété description
. Et ça y est, nos classes se conforment au protocole Animal
.
On a remplacé notre classe inutile et trop générique par un protocole qui remplit le même rôle !
En résumé
Un protocole est une liste d'exigences. Ces exigences peuvent être des méthodes ou des propriétés.
Souvenez-vous de la phrase suivante : un protocole est une entité qui déclare : "Si tu veux être ça, il faut que tu fasses ça !".
Un protocole se déclare avec la syntaxe suivante :
protocol NomDuProtocole {
func maMethode()
func maMethodeAvecParametresEtValeurDeRetour(param1: Type, Param2: Type) -> Type
var maPropriete: Type { get set }
var maProprieteEnLectureSeule: Type { get }
}
Une classe peut se conformer à un protocole en :
Adoptant le protocole.
Répondant à ses exigences.
// 1. On adopte le protocole
class MaClasse: MonProtocole {
// 2. On répond à ces exigences
func laMethodeDeMonProtocole() {
// Implémentation de la méthode définie par le protocole
}
}
Ceci constitue un premier tour d'horizon rapide des protocoles. Avant de les mettre en pratique dans notre application, nous allons asseoir un peu plus votre compréhension du sujet ! Cela se passe au prochain chapitre !