• 10 heures
  • Difficile

Ce cours est visible gratuitement en ligne.

course.header.alt.is_video

course.header.alt.is_certifying

J'ai tout compris !

Mis à jour le 05/04/2023

Sautez dans le Protocol Oriented Programming

Vous ne le voyez sans doute pas pour le moment, mais les protocoles changent vraiment votre approche du code. Au fur et à mesure de ce cours, je vais essayer de vous le faire percevoir.

Un aperçu du Protocol Oriented Programming

Le monde de la programmation est généralement sous le paradigme de l'orienté objet (OOP : Object Oriented Programming). On manipule des objets, et certains peuvent hériter les uns des autres. C'est ce que vous avez fait jusqu'à présent.

Mais en Swift, les protocoles sont tellement puissants que beaucoup considèrent que Swift est un langage de programmation orienté protocole (POP : Protocol Oriented Programming).

Pour comprendre ce que cela change, je vous propose de créer, dans notre Playground, une classe  Human  .

class Human {

   func speak() {

      print("Bonjour !")


   }

}

Un humain n'est pas un animal. En revanche, il a un nom et un prénom. Donc nous allons lui faire adopter le protocole  Nameable  .

class Human {
   var firstName: String = ""
   var lastName: String = ""

   func speak() {
      print("Bonjour !")
   }
}

extension Human: Nameable {
   func getFullName() -> String {
      return firstName + " " + lastName
   }
}

Vous voyez ici que le protocole  Nameable  peut s'appliquer à des objets qui n'ont à priori rien à voir entre eux. Il s'agit en effet d'une brique de fonctionnalité que je peux donner à n'importe quelle classe / structure / énumération.

Les extensions de protocole

Très bien, mais je dois redonner dans  Human  une implémentation de   getFullName  , qui en plus est la même que pour  Dog  ! C'est un peu pénible si ma brique de fonctionnalité devient plus complexe, non ?

Eh oui, c'est bien vu. Mais c'est de ma faute, je vous ai fait un petit mensonge tout à l'heure ! Je vous ai dit :

En Swift, c'est un peu faux. C'est précisément la raison pour laquelle les protocoles sont si importants dans ce langage !

En Swift, un protocole peut définir l'implémentation de ses méthodes.

Je répète : en Swift, un protocole peut définir l'implémentation de ses méthodes.

OK... ça a l'air chouette ?

C'est vraiment très très chouette. On va voir cela avec notre exemple de  Nameable  tout de suite. Voici la définition du protocole que l'on avait :

protocol Nameable {

   var firstName: String { get set }

   var lastName: String { get set }

   func getFullName() -> String

}

Et pour implémenter  getFullName  , on va faire ceci :

extension Nameable {

   func getFullName() -> String {

    return firstName + " " + lastName

   }

}

Donc pour que  Human  adopte  Nameable  ,  je peux simplifier le tout en :

class Human {
   var firstName: String = ""
   var lastName: String = ""

   func speak() {
      print("Bonjour !")
   }
}

extension Human: Nameable {
}

Et voilà le travail !  On utilise ce que l'on appelle une extension de protocole pour aller donner une implémentation par défaut de certaines de ses exigences. Je peux alors vraiment facilement ajouter une brique de fonctionnalité bien définie à n'importe quelle classe / structure / énumération !

Mais l’extension est vide, ici !

Tout à fait, et ce n’est pas grave tant que la conformance est respectée.

Et si je veux donner une autre implémentation, par exemple dans  Dog  , je fais comment ?! Parce que  override  marche seulement pour les classes !

En effet, on n'utilisera pas  override  dans ce genre de situation. Si, dans ma classe Dog , je veux une implémentation de  getFullName  différente de celle par défaut, eh bien, je n'ai qu'à la redonner :

class Dog {
   var firstName: String = ""
   var lastName: String = ""
}

extension Dog: Nameable {
   // Lorsque j'appelle cette méthode sur un chien,
   // c'est cette implémentation qui sera appelée,
   // et non celle définie par défaut.
   func getFullName() -> String {
    return "Waaaf \(firstName) ! WoofWoof \(lastName)"
   }

   // (...)
}

L'implémentation donnée par l'objet qui adopte le protocole sera toujours prioritaire par rapport aux implémentations par défaut. Cela est rassurant, puisque les protocoles sont à la base un moyen de s'abstraire de la façon dont sont implémentées les choses, en se limitant à connaître simplement les exigences.

L'orienté protocole en images !

Je vous propose les schémas suivants pour comparer les approches orientée objet et orientée protocole.

La Superclass envoie func method1, 2 et 3 dans SubClass 1, 2 et 3
Programmation Orientée Objet

En orienté objet, on partage des comportements grâce à l'héritage. Mais on est limité à une seule superclasse, et toutes les classes récupèrent forcément toutes les fonctionnalités de la superclasse. Cela oblige les sous-classes à une certaine homogénéité.

3 colonnes Myclass Mystruct MyEnum et 3 lignes  Protocle 1 2 et 3 avec les différentes func method aux intersections
Programmation Orientée Protocole

En orienté protocole, on partage des comportements grâce aux protocoles. C'est bien plus flexible, car :

  • On peut partager des comportements entre des classes / structures / énumérations.

  • Une même classe / structure / énumération peut adopter plusieurs protocoles.

  • En utilisant un protocole par fonctionnalité, on peut ajouter certaines fonctionnalités à certains objets sans les ajouter à d'autres.

  • On peut donner une implémentation par défaut de certaines fonctionnalités en utilisant les extensions de protocole.

L'orienté protocole dans le langage Swift

L'orienté protocole est au cœur de la conception de Swift. Le langage lui-même utilise de nombreux protocoles pour fonctionner.

Prenons un exemple avec  Equatable  .   Equatable  est un protocole qui permet de comparer deux valeurs pour voir si elles sont égales. Tous les types qui se conforment au protocole  Equatable  peuvent être utilisés avec les opérateurs  ==  et  !=  .

Il y a donc des centaines de types qui implémentent ce protocole, comme String, Int, Double, et beaucoup beaucoup d'autres.

Je vous propose d'essayer d'implémenter ce protocole pour notre classe  Human  . Commençons par adopter le protocole :

class Human: Nameable, Equatable { (...) }

Ensuite, le Playground affiche une erreur, car on ne se conforme pas au protocole.

 

Pour résoudre l'erreur, il faut répondre à la seule exigence de ce protocole en implémentant la méthode suivante :

static func == (lhs: Human, rhs: Human) -> Bool {

 

}

Cette fonction prend deux paramètres du type avec lequel on travaille, en l'occurrence  Human  . Ce sont les deux valeurs que nous essayons de comparer :

  • lhs  (left hand side) : la valeur à gauche du  ==  ;

  • rhs  (right hand side) : la valeur à droite du  ==  .

Et la fonction renvoie un booléen, résultat de l'égalité.

Pour implémenter cette fonction, il faut indiquer selon quelles conditions deux humains sont égaux. Nous allons dire que deux humains sont égaux s'ils ont le même nom et le même prénom, donc le même nom complet :

static func == (lhs: Human, rhs: Human) -> Bool {

   return lhs.getFullName() == rhs.getFullName()

}

Et voilà ! Maintenant, nous pouvons écrire le code suivant :

let human1 = Human()

let human2 = Human()

if human1 == human2 {

   print("Vous êtes les mêmes !")

}

Grâce aux protocoles, notre classe supporte maintenant la fonctionnalité  ==  !

En résumé

  • En programmation orientée protocole, on va utiliser les protocoles pour partager des fonctionnalités entre différents objets, de façon bien plus flexible.

  • Le langage Swift utilise beaucoup l'orienté protocole.

Maintenant que les protocoles sont, je l'espère, un peu plus clairs pour vous, nous allons nous attaquer à notre application et ajouter une liste !

Rendez-vous dans la prochaine partie  !

Exemple de certificat de réussite
Exemple de certificat de réussite