• 20 hours
  • Medium

Free online content available in this course.

course.header.alt.is_video

course.header.alt.is_certifying

Got it!

Last updated on 2/1/19

Protégez vos classes

Log in or subscribe for free to enjoy all this course has to offer!

En cette fin de cours, je vous propose un retour aux sources :

Philosoraptor
Philosoraptor

Ne vous inquiétez pas, je ne vais pas me lancer dans de grands débats philosophiques. Non je vais simplement vous rappeler l'intérêt d'une classe !

Au début de ce cours, je vous ai expliqué qu'une classe avait deux objectifs :

  • représenter un concept : c'est plus facile de manipuler un objet bus qu'un gros paquet de variables et de fonctions emmêlées.

  • cacher une implémentation : parfois nous n'avons pas besoin de savoir comment fonctionne précisément une méthode comme la méthode drive par exemple : on veut juste que le bus roule sur la route !

On a beaucoup parlé des moyens pour arriver au premier objectif, mais assez peu des outils qui servent le deuxième et c'est le moment de se rattraper !

Le contrôle d'accès

Le contrôle d'accès (ou access control en anglais) est un moyen de limiter l'accès aux constituants d'une classe, d'un module ou d'un fichier. Pour bien comprendre comment ça marche, il me faut vous expliquer d'abord ce qu'est un module et ce qu'est un fichier source.

Commençons par le plus simple, un fichier source, c'est tout simplement un fichier. Si vous allez dans le navigateur de Xcode, vous verrez l'ensemble des fichiers d'un projet, chaque ligne correspond à un fichier source. Dans notre Playground, vous avez tout écrit dans un unique fichier.

Un module, c'est un petit paquet de fichiers qui forme un tout. Donc cela peut-être une application iPhone ou un framework. Les sources du Playground forment également un module.

Le concept de module et de fichier source est au coeur du système de contôle d'accès de Swift.

Les niveaux de contrôle

Avec le contrôle d'accès, on va pouvoir limiter l'accès aux classes et à leur contenu. Par exemple, on va pouvoir dire qu'une fonction dans une classe est uniquement accessible dans cette classe.

Il y existe 4 niveaux de contrôle, du plus permissif au plus restrictif :

  • Public (public) : les éléments publiques sont accessibles partout depuis n'importe quel module et n'importe quel fichier.

  • Interne (internal) : les éléments internes sont accessibles dans tous les fichiers du module dans lequel ils se trouvent. En revanche, ils ne sont pas accessibles à l'extérieur de ce module.

  • Privé au fichier (fileprivate) : les éléments privés au fichier ne sont accessibles que dans le fichier dans lequel ils sont définis.

  • Privé (private) : les éléments privés ne sont accessibles que dans le contexte dans lequel ils sont définis. Par exemple, si une méthode est privée, elle ne pourra être utilisée qu'à l'intérieur de la classe dans laquelle elle se trouve.

Par défaut, tous les éléments sont au niveau : Interne. Donc tous les éléments sont par défaut disponibles dans le module qui les contient.

Voici un schéma qui résume les différents niveaux de contrôle d'accès.

Mise en pratique

Assez de théorie, je sais que vous avez besoin de voir concrètement ce que ça donne ! Alors, allons-y. Prenons notre classe SchoolBus :

class SchoolBus: Bus {
    var schoolName = ""

    override func drive(road: Road) {
        // (...)
    }

    func shouldPickChildren() -> Bool {
        // (...)
    }

    func pickChildren(from roadSection: RoadSection) {
        // (...)
    }

    func dropChildren() {
        // (...)
    }
}

Nous avons donc 4 méthodes dans cette classe. Ces méthodes sont par défaut au niveau de contrôle interne et disponibles dans tout le module, en l'occurrence le Playground.

Or, les fonctions shouldPickChildrenpickChildren et dropChildren n'ont pas besoin d'être accessibles partout dans le Playground. Elles sont uniquement utilisées à l'intérieur de la classe dans la méthode drive. Donc nous pouvons les marquer comme privés pour limiter leur utilisation à l'intérieur de la classe. Pour cela, il suffit d'écrire le nom du niveau de contrôle souhaité devant la déclaration de la méthode :

class SchoolBus: Bus {
    var schoolName = ""

    override func drive(road: Road) {
        // (...)
    }

    private func shouldPickChildren() -> Bool {
        // (...)
    }

    private func pickChildren(from roadSection: RoadSection) {
             // (...)
    }

    private func dropChildren() {
            // (...)
    }
}

Maintenant si j'essaie d'utiliser ces fonctions en dehors de la classe, cela n'est pas possible :

var unBusScolaire = SchoolBus(driverName: "Joe")
unBusScolaire.dropChildren() // ERREUR

Nous venons de protéger notre classe SchoolBus ! Si quelqu'un l'utilise désormais, il n'aura pas accès à ces méthodes et cela lui évitera de faire n'importe quoi avec. C'est la raison pour laquelle je vous recommande de toujours définir des niveaux de contrôle les plus stricts possibles.

Au-delà de la sécurité, le contrôle d'accès est très important pour la lisibilité du code. Le contrôle d'accès permet de rendre très clair son intention. Si un développeur doit lire le contenu de ma classe SchoolBus, il saura en un clin d'œil que la classe ne contient qu'une fonction utilisable à l'extérieur et que les autres, par conséquent, sont uniquement utilisées dans la classe.

Pour vous donner une preuve, je vous invite à aller jeter un œil aux sources du Playground. Les sources du Playground sont considérées comme un module indépendant de ce dernier. Du coup, le contrôle d'accès y joue un rôle primordial. Même si vous ne comprendrez pas le détail des implémentations, je suis certain que le contrôle d'accès vous donnera déjà une idée du fonctionnement global de ces sources.

Vous verrez dans le fichier Canvas.swift que la classe est définie comme public. En effet, on en a besoin dans le Playground. Et seulement 5 fonctions sont définies comme publique : ce sont celles que vous connaissez.

Classe et contrôle d'accès

Pour l'instant, nous avons seulement vu l'exemple d'une méthode privée. Mais sachez que l'on peut, de la même manière, modifier le contrôle d'accès des propriétés et des classes. Pour cela, il suffit d'écrire le mot-clé correspondant au niveau de contrôle souhaité devant la déclaration :

public class UneClassePublique {
    internal var unePropriétéInterne = 0

    fileprivate let uneConstantePrivéAuFichier = "Coucou"

    private func uneFonctionPrivé() {

    }
}

Pour terminer ce chapitre, je vous donne deux petites règles :

1. Hiérarchie du contrôle Un élément ne peut pas avoir un niveau plus permissif que celui qui le contient. Par exemple, je ne peux pas avoir une propriété au niveau public dans une classe au niveau privé :

private class MaClassePrivé {
    public var maPropriétéPublic = 0 // Impossible
}

2. Contrôle par défaut Le niveau de contrôle par défaut est interne comme je vous l'ai dit tout à l'heure. Mais tous les membres d'une classe (propriétés et méthodes) ont par défaut le niveau de contrôle de la classe dans laquelle ils sont définis. Donc si une classe a pour niveau fileprivate, tous ses membres auront par défaut le niveau de contrôle fileprivate. Par exemple :

fileprivate class MaClassePrivéAuFichier {
    var unePropriétéImplicitementPrivéAuFichier = 0 // niveau de contrôle fileprivate par défaut
    private var unePropriétéExplicitementPrivé = 0 // niveau de contrôle private car précisé

}

Exercice

Entraînez-vous avec cet exercice !

En résumé

  • Le contrôle d'accès permet de limiter l'accès à certains éléments d'un programme

  • Il existe 4 niveaux de contrôle d'accès : public, interne, privé au fichier, privé

  • Le contrôle d'accès permet d'améliorer la lisibilité du code en exprimant son intention concernant un élément du programme.

  • Un élément ne peut pas avoir un niveau plus permissif que celui qui le contient.

  • Tous les membres d'une classe ont par défaut le niveau de contrôle de la classe dans laquelle ils sont définis, sauf si la classe est public.

Example of certificate of achievement
Example of certificate of achievement