Protocoles

Comment définir un ensemble de règles pour que vos classes, structures et énumérations se comportent d'une certaine manière.

En Swift, les protocoles sont des types essentiels qui définissent un ensemble de méthodes et de propriétés que les classes, les structures (structs) ou les énumérations (enums) peuvent adopter. Les protocoles sont utilisés pour garantir que les types respectent certaines spécifications ou comportements, et ils permettent une programmation plus flexible et décentralisée. Ils jouent un rôle crucial dans la conception de code modulaire et réutilisable.

1. Définition d'un Protocole

Un protocole est défini en utilisant le mot-clé protocol, suivi du nom du protocole et d'une liste de méthodes, propriétés, et autres exigences.

protocol Drivable {
    var hasWheels: Bool { get }
    func startEngine()
    func drive()
}

2. Adopter un Protocole

Pour qu'une classe, une structure ou une énumération adopte un protocole, elle doit déclarer qu'elle adopte le protocole en utilisant le mot-clé :, et ensuite implémenter toutes les méthodes et propriétés requises par ce protocole.

class Car: Drivable {
    var hasWheels: Bool = true
    func startEngine() {
        print("Le moteur de la voiture démarre.")
    }
    func drive() {
        print("La voiture roule.")
    }
}

let myCar = Car()
myCar.startEngine()  // Affiche : "Le moteur de la voiture démarre."
myCar.drive()  // Affiche : "La voiture roule."

3. Propriétés Requises

Les protocoles peuvent exiger que les types adoptants implémentent certaines propriétés, qu'elles soient variables (var) ou constantes (let). Les propriétés définies dans un protocole n'ont pas de valeurs par défaut et doivent être implémentées dans les types conformes.

protocol Identifiable {
    var id: String { get }
}

struct User: Identifiable {
    var id: String
}

let user = User(id: "12345")
print("User ID: \(user.id)")  // Affiche : "User ID: 12345"

4. Méthodes Requises

Les protocoles peuvent spécifier des méthodes que les types adoptants doivent implémenter. Ces méthodes n'ont pas de corps dans le protocole, juste une signature.

protocol Greetable {
    func greet()
}

struct Person: Greetable {
    var name: String
    func greet() {
        print("Bonjour, je m'appelle \(name).")
    }
}

let person = Person(name: "Alice")
person.greet()  // Affiche : "Bonjour, je m'appelle Alice."

5. Protocoles avec Méthodes et Propriétés Optionnelles

Les protocoles peuvent définir des méthodes et des propriétés optionnelles en utilisant l'annotation @objc et en héritant de NSObjectProtocol. Ces protocoles sont principalement utilisés en interopérabilité avec Objective-C.

@objc protocol Displayable {
    @objc optional func display()
}

class Item: NSObject, Displayable {
    func display() {
        print("Affichage de l'objet.")
    }
}

let item = Item()
item.display?()  // Affiche : "Affichage de l'objet."

6. Protocoles et Extensions

Les protocoles peuvent être étendus pour fournir des implémentations par défaut de leurs méthodes et propriétés. Cela permet de définir des comportements communs à plusieurs types qui adoptent le même protocole.

protocol Describable {
    func describe() -> String
}

extension Describable {
    func describe() -> String {
        return "Ceci est un objet décrivable."
    }
}

struct Book: Describable {
    var title: String
}

let book = Book(title: "Swift Programming")
print(book.describe())  // Affiche : "Ceci est un objet décrivable."

7. Protocoles Composés

Swift permet de combiner plusieurs protocoles en un seul en utilisant la syntaxe des protocoles composés. Cela permet de créer un nouveau protocole qui adopte plusieurs autres protocoles.

protocol Readable {
    func read()
}

protocol Writable {
    func write()
}

typealias ReadWrite = Readable & Writable

struct Document: ReadWrite {
    func read() {
        print("Lecture du document.")
    }
    func write() {
        print("Écriture du document.")
    }
}

let doc = Document()
doc.read()  // Affiche : "Lecture du document."
doc.write()  // Affiche : "Écriture du document."

8. Protocoles et Héritage

Les protocoles peuvent hériter d'autres protocoles, permettant de créer des protocoles plus spécifiques à partir de protocoles plus généraux.

protocol Animal {
    func makeSound()
}

protocol Pet: Animal {
    func play()
}

struct Dog: Pet {
    func makeSound() {
        print("Le chien aboie.")
    }
    func play() {
        print("Le chien joue.")
    }
}

let dog = Dog()
dog.makeSound()  // Affiche : "Le chien aboie."
dog.play()  // Affiche : "Le chien joue."

9. Protocoles et Génériques

Les protocoles peuvent être utilisés avec des types génériques pour créer des fonctions et des types qui sont flexibles et peuvent travailler avec différents types conformes à un protocole donné.

protocol Summable {
    func sum() -> Int
}

struct Numbers: Summable {
    var numbers: [Int]
    func sum() -> Int {
        return numbers.reduce(0, +)
    }
}

func printSum(of summable: T) {
    print("La somme est \(summable.sum()).")
}

let numbers = Numbers(numbers: [1, 2, 3, 4, 5])
printSum(of: numbers)  // Affiche : "La somme est 15."