Programmation orientée protocole dans Swift 2

introduction

Avec la sortie de Swift 2, Apple a ajouté une gamme de nouvelles fonctionnalités au nouveau langage de programmation Swift. L’un des plus importants, cependant, a été la refonte des protocoles. Les fonctionnalités améliorées disponibles avec les protocoles Swift permettent un nouveau type de programmation, la programmation orientée protocole. Cela contraste avec le style de programmation orienté objet plus commun que beaucoup d’entre nous sont habitués à utiliser..

Dans ce tutoriel, je vais vous montrer les bases de la programmation orientée protocole dans Swift et en quoi elle diffère de la programmation orientée objet..

Conditions préalables

Ce tutoriel nécessite que vous exécutiez Xcode 7 ou une version ultérieure, ce qui inclut la prise en charge de la version 2 du langage de programmation Swift..

1. Notions de base sur le protocole

Si vous n'êtes pas déjà familiarisé avec les protocoles, ils constituent un moyen d'étendre les fonctionnalités d'une classe ou d'une structure existante. Un protocole peut être considéré comme un plan ou une interface définissant un ensemble de propriétés et de méthodes. Une classe ou une structure conforme à un protocole est nécessaire pour renseigner ces propriétés et méthodes avec respectivement des valeurs et des implémentations.

Il convient également de noter que chacune de ces propriétés et méthodes peut être désignée comme facultative, ce qui signifie que les types conformes ne sont pas obligés de les implémenter. Une définition de protocole et une conformité de classe dans Swift pourraient ressembler à ceci:

protocol Welcome var welcomeMessage: String get set, facultatif func welcome (), classe Welcomer: Welcome var welcomeMessage = "Bonjour le monde!" func welcome () print (welcomeMessage)

2. Un exemple

Pour commencer, ouvrez Xcode et créez un nouveau terrain de jeu pour iOS ou OS X. Une fois que Xcode a créé le terrain de jeu, remplacez son contenu par ce qui suit:

protocol Drivable var topSpeed: Int get protocol Réversible var reverseSpeed: Int get protocole Transport var seatCount: Int get

Nous définissons trois protocoles, chacun contenant une propriété. Ensuite, nous créons une structure conforme à ces trois protocoles. Ajoutez le code suivant au terrain de jeu:

struct Car: pilotable, réversible, transport var topSpeed ​​= 150 var reverseSpeed ​​= 20 var seatCount = 5

Vous avez peut-être remarqué qu'au lieu de créer une classe conforme à ces protocoles, nous avons créé une structure. Nous faisons cela pour éviter l’un des problèmes typiques de la programmation orientée objet., références d'objet.

Imaginons, par exemple, que vous ayez deux objets, A et B. A crée lui-même des données et conserve une référence à ces données. A partage ensuite ces données avec B par référence, ce qui signifie que les deux objets ont une référence au même objet. Sans que A sache, B modifie les données d'une manière ou d'une autre.

Bien que cela ne semble pas être un gros problème, il peut arriver que A ne s’attende pas à ce que les données soient modifiées. L'objet A peut trouver des données qu'il ne sait pas manipuler ou traiter. Ceci est un risque commun de références d'objet.

Dans Swift, les structures sont passées par valeur plutôt que par référence. Cela signifie que, dans l'exemple ci-dessus, si les données créées par A étaient regroupées sous la forme d'une structure au lieu d'un objet et partagées avec B, les données seraient copiées au lieu d'être partagées par référence. Cela donnerait alors à A et à B une copie unique de la même donnée. Un changement effectué par B n’affecterait pas la copie gérée par A.

Briser le DrivableRéversible, et Transport les composants en protocoles individuels permettent également un niveau de personnalisation supérieur à celui de l'héritage de classe traditionnel. Si vous avez lu mon premier tutoriel sur le nouveau framework GameplayKit dans iOS 9, ce modèle orienté protocole est très similaire à la structure Entities and Components utilisée dans le framework GameplayKit..

En adoptant cette approche, les types de données personnalisés peuvent hériter des fonctionnalités de plusieurs sources plutôt que d'une seule classe. En gardant à l’esprit ce que nous avons jusqu’à présent, nous pourrions créer les classes suivantes:

  • une classe avec des composants du Drivable et Réversible les protocoles
  • une classe avec des composants du Drivable et Transportable les protocoles
  • une classe avec des composants du Réversible et Transportable les protocoles

Avec la programmation orientée objet, le moyen le plus logique de créer ces trois classes consiste à hériter d'une superclasse contenant les composants des trois protocoles. Cependant, cette approche a pour résultat que la super-classe est plus compliquée que nécessaire et que chacune des sous-classes hérite de plus de fonctionnalités que nécessaire..

3. Extensions de protocole

Tout ce que je vous ai montré jusqu'à présent est possible dans Swift depuis sa publication en 2014. Ces mêmes concepts axés sur les protocoles auraient même pu être appliqués aux protocoles Objective-C. Cependant, en raison des limitations qui existaient auparavant sur les protocoles, une véritable programmation orientée protocole était impossible avant que plusieurs fonctionnalités clés aient été ajoutées au langage Swift dans la version 2. L'une des plus importantes de ces fonctionnalités extensions de protocole, comprenant extensions conditionnelles.

Tout d'abord, étendons la Drivable protocole et ajouter une fonction pour déterminer si un particulier Drivable est plus rapide qu'un autre. Ajoutez ce qui suit à votre terrain de jeu:

extension Drivable func isFasterThan (item: Drivable) -> Bool return self.topSpeed> item.topSpeed let sedan = Voiture () let sportsCar = Voiture (topSpeed: 250, reverseSpeed: 25, seatCount: 2) sedan.isFasterThan (voiture de sport)

Vous pouvez voir que, lorsque le code du terrain de jeu est exécuté, il génère une valeur de fauxcomme votre sedan la voiture a un défaut vitesse de pointe de 150, qui est moins que le voiture de sport.

Vous avez peut-être remarqué que nous avons fourni une fonction définition plutôt qu'une fonction déclaration. Cela semble étrange, car les protocoles ne sont censés contenir que des déclarations. Droite? C’est une autre caractéristique très importante des extensions de protocole dans Swift 2., comportements par défaut. En étendant un protocole, vous pouvez fournir une implémentation par défaut pour les fonctions et les propriétés calculées afin que les classes conformes au protocole n'aient pas à le faire..

Ensuite, nous allons définir un autre Drivable extension de protocole, mais cette fois, nous le définirons uniquement pour les types de valeur qui sont également conformes à la norme. Réversible protocole. Cette extension contiendra alors une fonction qui détermine quel objet a la meilleure plage de vitesse. Nous pouvons y parvenir avec le code suivant:

extension Drivable où Self: Réversible func hasLargerRangeThan (item: Self) -> Bool return (self.topSpeed ​​+ self.reverseSpeed)> (item.topSpeed ​​+ item.reverseSpeed) sportsCar.hasLargerRangeThan (sedan)

le Soi mot-clé, écrit avec une majuscule "S", est utilisé pour représenter la classe ou la structure conforme au protocole. Dans l'exemple ci-dessus, le Soi mot-clé représente le Voiture structure.

Après avoir exécuté le code du terrain de jeu, Xcode affichera les résultats dans la barre latérale à droite, comme indiqué ci-dessous. Notez que voiture de sport a une gamme plus large que sedan.

4. Travailler avec la bibliothèque Swift Standard

Définir et étendre vos propres protocoles peut s'avérer très utile, mais la véritable puissance des extensions de protocole est évidente lorsque vous travaillez avec la bibliothèque standard Swift. Cela vous permet d’ajouter des propriétés ou des fonctions aux protocoles existants, tels que CollectionType (utilisé pour des choses comme des tableaux et des dictionnaires) et Équitable (être capable de déterminer quand deux objets sont égaux ou non). Avec les extensions de protocole conditionnelles, vous pouvez également fournir des fonctionnalités très spécifiques pour un type d'objet spécifique conforme à un protocole..

Dans notre terrain de jeu, nous allons étendre la CollectionType protocole et créer deux méthodes, une pour obtenir la vitesse maximale moyenne des voitures dans un Voiture tableau et un autre pour la vitesse inverse moyenne. Ajoutez le code suivant à votre terrain de jeu:

extension CollectionType où Self.Generator.Element: Drivable func averageTopSpeed ​​() -> Int var total = 0, count = 0 pour l'élément en auto total + = item.topSpeed ​​count ++ return (total / count) func averageReverseSpeed(items: T) -> Int var total = 0, count = 0 pour item dans les items total + = item.reverseSpeed ​​count ++ return (total / count) let cars = [voiture (), berline, voiture de sport] voitures .averageTopSpeed ​​() averageReverseSpeed ​​(voitures)

L'extension de protocole qui définit le averageTopSpeed Cette méthode tire parti des extensions conditionnelles de Swift 2. En revanche, la moyenneReverseSpeed La fonction que nous définissons directement ci-dessous est un autre moyen d’obtenir un résultat similaire en utilisant les génériques Swift. Personnellement, je préfère le plus propre CollectionType extension de protocole, mais c'est à la préférence personnelle.

Dans les deux fonctions, nous parcourons le tableau, additionnons le montant total, puis renvoyons la valeur moyenne. Notez que nous gardons manuellement le nombre d’éléments dans le tableau, car lorsqu’on travaille avec CollectionType plutôt que régulier Tableau les éléments de type, le compter la propriété est un Self.Index.Distance tapez une valeur plutôt qu'un Int.

Une fois que votre terrain de jeu a exécuté tout ce code, vous devriez voir une vitesse maximale moyenne de sortie de 183 et une vitesse moyenne inverse de 21.

5. Importance des cours

Bien que la programmation orientée protocole soit un moyen très efficace et évolutif de gérer votre code dans Swift, il existe toujours des raisons parfaitement valables pour utiliser des classes lors du développement dans Swift:

Rétrocompatibilité

La majorité des kits de développement logiciel (SDK) iOS, watchOS et tvOS sont écrits en Objective-C, en utilisant une approche orientée objet. Si vous devez interagir avec l'une des API incluses dans ces kits de développement logiciel, vous devez utiliser les classes définies dans ces kits de développement logiciel..

Référencer un fichier externe ou un élément

Le compilateur Swift optimise la durée de vie des objets en fonction du moment et du lieu d'utilisation. La stabilité des objets de classe signifie que vos références à d'autres fichiers et éléments resteront cohérentes..

Références d'objet

Les références d'objet sont exactement ce dont vous avez parfois besoin, par exemple si vous insérez des informations dans un objet particulier, tel qu'un moteur de rendu graphique. L'utilisation de classes avec partage implicite est importante dans des situations comme celle-ci, car vous devez vous assurer que le rendu auquel vous envoyez les données est toujours le même rendu qu'auparavant..

Conclusion

J'espère qu'à la fin de ce didacticiel, vous pourrez voir le potentiel de la programmation orientée protocole dans Swift et comment l'utiliser pour rationaliser et étendre votre code. Bien que cette nouvelle méthodologie de codage ne remplace pas entièrement la programmation orientée objet, elle apporte un certain nombre de nouvelles possibilités très utiles..

Des comportements par défaut aux extensions de protocole, la programmation orientée protocole de Swift va être adoptée par de nombreuses API futures et va complètement changer la façon dont nous pensons au développement logiciel..

Comme toujours, assurez-vous de laisser vos commentaires dans les commentaires ci-dessous.