• 12 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 01/02/2019

Sautez dans le Protocol Oriented Programming

Connectez-vous ou inscrivez-vous gratuitement pour bénéficier de toutes les fonctionnalités de ce cours !

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: Nameable {
    var firstName: String = ""
    var lastName: String = ""

    func getFullName() -> String {
        return firstName + " " + lastName
    }

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

Vous voyez ici que le protocole Nameable peut s'appliquer à des objets qui n'ont a 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 :

Un protocole peut définir des méthodes, mais ce sont les classes qui les implémentent.

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: Nameable {
    var firstName: String = ""
    var lastName: String = ""

    // Pas besoin d'implémenter getFullName, mais toute instance de Human pourra l'appeler !!

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

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 si je veux donner une autre implémentation, par exemple dans Dog, je fais comment ?! Parce que override marche pour les classes seulement !

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: Nameable {
    var firstName: String = ""
    var lastName: String = ""

    // Lorsque j'appelle cette méthode sur un chien,
    // c'est cette implémentation qui sera appellé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 image !

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

Programmation Orientée Objet
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é.

Programmation Orientée Protocole
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 EquatableEquatable 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 rajouter une liste !

Rendez-vous dans la prochaine partie ;) !

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