Programmation rapide 101 : les génériques - un guide pratique

Si vous avez lu la documentation d'Apple sur les génériques et que vous vous êtes demandé comment utiliser cette technologie dans vos propres projets, ce billet est pour vous ! Vous y apprendrez comment tirer pleinement parti des génériques dans votre code quotidien et comment éviter la fonte de caractères constante qui résulte généralement de la création de code généralisé.

Maîtrisez votre iPhone en une minute par jour :

Inscrivez-vous à la newsletter "Tip of the Day" de iphonologie et nous vous enverrons chaque jour un conseil pour gagner du temps et tirer le meilleur parti de votre iPhone ou iPad.

Le problème du code généralisé

Tout bon développeur d'applications a besoin d'une base de code générique qu'il peut utiliser à partir des projets iOS qu'il crée. Par exemple, dans cette série de blogs, j'ai utilisé la classe mmBusinessObject dans plusieurs projets. Cette classe encapsule les données de base, et l'utiliser dans vos projets vous aide à éviter le code répétitif.

Cependant, l'un des inconvénients de l'utilisation d'un code généralisé est que vous devez souvent passer de types généraux à des types plus spécifiques à plusieurs reprises dans votre projet.

Examinons un exemple qui démontre ce problème. Vous pouvez télécharger l'exemple de projet pour cet article à ce lien .

  1. Ouvrez le projet GenericsDemo dans Xcode.
  1. Le navigateur de projet contient la plupart des classes indiquées dans le diagramme de classes de la figure 1 .

Figure 1 - Diagramme des classes d'objets commerciaux

Sur le côté gauche du diagramme se trouvent les classes d'entreprises. Personne et Société sont des sous-classes de ABusinessObject , qui est à son tour une sous-classe de mmBusinessObject .

Sur le côté droit du diagramme se trouvent les classes d'entités. PersonEntity et CompanyEntity sont des sous-classes de NSManagedObject .

La classe mmBusinessObject contient des méthodes qui créent et manipulent des entités. Comme il s'agit d'une classe personnalisée au niveau du cadre, ses méthodes renvoient des instances de NSManagedObject . Cela vous permet d'utiliser la classe mmBusinessObject de n'importe quel projet iOS.

Les classes d'entreprises spécifiques à votre projet, telles que Personne et Société , héritent de ces méthodes qui renvoient des instances de NSManagedObject . Cependant, elles doivent décomposer les objets généralisés NSManagedObject en objets PersonEntity et CompanyEntity , pour accéder à des propriétés spécifiques à l'entité telles que firstName , lastName , et personID ( Figure 1 ).

Examinons cela de plus près en code afin que vous compreniez bien la relation entre ces classes et le problème du code généralisé.

  1. Sélectionnez le fichier mmBusinessObject.swift dans le navigateur de projet. En haut du fichier se trouve la méthode createEntity , qui renvoie un NSManagedObject :

En dessous de cette méthode se trouve la méthode getAllEntities , qui retourne un tableau de NSManagedObjects :

  1. Les classes Personne et Société héritent de ces méthodes et renvoient donc les mêmes types que mmBusinessObject . Pour vérifier cela, sélectionnez le fichier ViewController.swift dans le navigateur de projet. Vérifiez le code dans la méthode viewDidLoad :

Ce code crée un objet Person , puis appelle ses méthodes getAllEntities et createEntity . Le deuxième bloc de code crée un objet Company et appelle les mêmes méthodes sur cet objet.

  1. Cliquez sur la variable personList et allez ensuite dans l'inspecteur d'aide rapide sur le côté droit de la fenêtre Xcode ( Figure 2 ).

Figure 2 - personList contient un ensemble d'objets gérés par le NSM .

Il indique que le type de la variable est un tableau de NSManagedObjects . Il est déterminé par l'inférence de type de Swift, basée sur le type de retour de la méthode getAllEntities .

  1. Cliquez sur la variable personEntity . L'inspecteur de l'aide rapide indique qu'elle est de type NSManagedObject , sur la base du type de retour de la méthode createEntity ( Figure 3 ).

Figure 3 - personneL'entité est de type NSManagedObject .

  1. Cliquez sur la variable companyList et vous pouvez voir qu'elle contient un tableau de NSManagedObjects . Cliquez ensuite sur la variable companyEntity et vous pouvez voir qu'elle est du type NSManagedObject . C'est parce que les méthodes getAllEntities et createEntity sont héritées à la fois par les classes Person et Company , et que les deux méthodes renvoient des objets du type NSManagedObject .
  1. Essayons d'accéder aux propriétés de l'objet PersonEntity . Sous le code qui crée un objet PersonEntity , ajoutez la dernière ligne de code dans Figure 4 , qui tente d'accéder à la propriété firstName .

Figure 4 - Essayez d'accéder à la propriété PersonEntity de l'objet firstName .

La propriété firstName n'apparaît pas dans la liste de complétion des codes car la variable personEntity est du type NSManagedObject . Vous devez descendre à PersonEntity pour voir cette propriété.

  1. Supprimez le code partiel que vous venez d'entrer, et ajoutons du code pour effectuer le downcast.

Une solution non générique

Examinons une option de réduction des valeurs de retour de ces méthodes héritées sans les génériques .

  1. Dans la méthode viewDidLoad , vous pourriez changer le code comme suit (mais ne le faites pas) :

Cette solution fonctionne. La valeur de retour de chaque méthode est ramenée au type d'entité correct. Cependant, c'est une véritable douleur dans le cou. Chaque fois que vous appelez une méthode d'objet métier (et il y a beaucoup plus de méthodes que ce que je montre ici), vous devez écrire du code qui effectue une décompression.

C'est beaucoup moins de travail que de demander à chaque objet d'affaires de renvoyer le type descendant à la place. Pour ce faire, vous ajoutez des méthodes à la Personne et aux classes qui remplacent les méthodes existantes ou ajoutez des méthodes complètement nouvelles pour renvoyer les types souhaités. Par exemple :

C'est une solution plus acceptable. Elle permet d'atteindre l'objectif de restitution des objets de l'entreprise PersonEntity et CompanyEntity . Cependant, lorsque vous considérez toutes les méthodes de la classe mmBusinessObject (environ 20 dans la version complète) et que vous multipliez cela par le nombre d'objets métier dans chaque projet (20-30 dans un grand projet), vous vous retrouvez rapidement à outrepasser plusieurs centaines de méthodes. Il doit y avoir une meilleure méthode !

Entrez les méthodes génériques.

La solution de la méthode générique

Il existe plusieurs façons d'intégrer des génériques dans vos projets. Commençons par une approche minimaliste et créons une méthode générique.

  1. Puisque nous allons changer la classe mmBusinessObject pour implémenter des génériques, laissons la classe actuelle intacte, et éditons mmBusinessObjectGeneric , qui est une copie de la classe avec un nom différent.
  1. Sélectionnez le fichier ABusinessObject.swift dans le navigateur de projet. N'oubliez pas que cette classe est la superclasse des classes Personne et Société ( Figure 1 ), donc le changement de sa superclasse modifie également l'héritage de ces autres classes. Près du sommet de la classe, changez la superclasse en mmBusinessObjectGeneric :

  1. Sélectionnez le fichier mmBusinessObjectGeneric.swift dans le navigateur de projet.
  1. Dans la partie supérieure du fichier de code, localisez la méthode createEntity ( Figure 5 ).

Figure 5 - La méthode createEntity sous sa forme "spécifique"

Cette méthode n'est pas générique. Elle retourne une valeur spécifique du type NSManagedObject . Lorsque nous la convertissons en une méthode générique, tout code qui appelle cette méthode peut spécifier le type de la valeur retournée.

  1. Faisons le premier pas pour rendre cette méthode générique. Entre le nom de la méthode et les parenthèses, ajoutez :

Il s'agit d'un paramètre de type. Il déclare la lettre T comme caractère de remplacement pour un type dans cette méthode. Le concept est similaire à l'utilisation de n comme caractère de remplacement dans une équation. Le paramètre de type est remplacé par un type réel lorsque la méthode est appelée.

La lettre T n'a rien de magique. Vous pouvez spécifier une lettre ou un ensemble de lettres différent comme caractère de remplacement, mais il est classique d'utiliser la lettre T, qui signifie "Type".

Lorsque vous déclarez un paramètre de type pour une méthode, il peut être utilisé pour spécifier :

  • Les types de paramètres de la méthode
  • Le type de retour de la méthode
  • Types dans le corps de la méthode

Utilisons maintenant les paramètres de type de cette méthode.

  1. Remplacez le type de retour de la méthode createEntity NSManagedObject par le caractère de remplacement T :

  1. Ensuite, changez l'opération comme NSManagedObject au bas de la méthode en comme T :

La figure 6 montre les trois endroits où vous avez utilisé le paramètre type. Vous êtes maintenant prêt à appeler cette méthode générique pour voir comment elle fonctionne à l'exécution.

Figure 6 - createEntity comme méthode générique

Test de la méthode générique

  1. Sélectionnez ViewController.swift dans le navigateur de projet.
  1. Appuyez sur Command+B pour construire le projet. Cela affiche une erreur Impossible de convertir le type de l'expression '()' en type 'T' pour ces lignes de code :

Le compilateur se plaint du fait que vous n'avez pas fourni suffisamment d'informations pour déterminer le type qu'il doit remplacer le caractère de remplacement T dans la méthode createEntity .

  1. Si vous avez utilisé des génériques dans d'autres langues, votre premier réflexe peut être de passer le type à la méthode entre crochets comme ceci :

Cependant, cela produit l'erreur de compilation . Il n'est pas possible de spécialiser explicitement une fonction générique .

  1. Même si vous ne pouvez pas spécifier explicitement le type de cette manière, vous pouvez spécifier le type de la variable où la valeur de retour de la méthode est stockée :

Cela permet au compilateur de déduire que PersonEntity peut remplacer le caractère de remplissage T comme type de retour de la méthode createEntity . Allez-y, faites ce changement.

  1. Faites de même pour l'objet CompanyEntity :

  1. Pour voir ce code en action, fixez un point d'arrêt sur la ligne de code indiquée dans Figure 7 .

Figure 7 - Définir un point d'arrêt sur l'appel à createEntity .

  1. Cliquez sur le bouton "Run" de Xcode et l'exécution s'arrête sur le point d'arrêt que vous avez défini.
  1. Cliquez sur le bouton "Step Into" dans la barre d'outils de débogage en haut de la zone de débogage. Cela déplace l'exécution vers la méthode createEntity . La vue des variables montre que le type de placeholder T est maintenant PersonEntity ( Figure 8 ). C'est le générique au travail !

Figure 8 - Le T placeholder type est CompanyEntity .

  1. Cliquez sur le bouton "Step Out" pour exécuter la méthode createEntity . Cela vous renvoie à la méthode viewDidLoad , où vous pouvez voir dans la vue des variables que la variable personEntity est du type PersonEntity ( Figure 9 ).

Figure 9 - createEntity renvoie un type PersonEntity .

Dans cet exemple, Swift a pu déterminer le type correct à remplacer par T en utilisant l'inférence de type sur la valeur de retour de la méthode. Cela fonctionne également pour les méthodes avec des paramètres génériques, comme vous le verrez dans un instant.

Bien que les génériques fonctionnent correctement dans cet exemple, vous devez quand même spécifier le type de la variable de retour. Cela ne résout pas complètement le problème initial qui consiste à devoir déclarer explicitement les types lors de l'accès aux méthodes d'objets métier. Il doit y avoir un autre moyen !

Entrez les classes génériques.

Déclarer des classes génériques

Si vous avez utilisé la classe Swift's Array ou Dictionary, vous avez déjà utilisé des classes génériques. Lorsque vous déclarez un Array ou un Dictionary, vous spécifiez le type de valeurs contenues dans la collection. Par exemple, le code suivant déclare un tableau de type String :

Swift vous permet de déclarer vos propres classes génériques personnalisées. Essayons avec mmBusinessObjectGeneric .

  1. Sélectionnez mmBusinessObjectGeneric.swift dans le navigateur de projet.
  1. Ajoutez la déclaration de classe générique suivante en haut du fichier de code :

Ce paramètre de type déclare que T est utilisé comme caractère de remplacement pour un type dans la classe entière.

Le : NSManagedObject dans la déclaration est une contrainte de type . Elle spécifie mmBusinessObjectGeneric ne fonctionne qu'avec les types de NSManagedObject ou ses sous-classes. Cela permet à la classe de fonctionner avec les objets PersonEntity et ConpanyEntity (qui sont des sous-classes de NSManagedObject ), mais pas avec des objets d'un type complètement différent tels que String ou Integer .

  1. Puisque la déclaration de classe générique englobe la classe entière, nous pouvons maintenant supprimer la déclaration de type générique de la méthode createEntity .
  1. Appuyez sur Command+B pour construire le projet et vous obtiendrez plusieurs erreurs de compilation.
  1. Pour examiner l'une de ces erreurs, dans le navigateur de projet, sélectionnez le fichier ABusinessObject.swift . Vous pouvez voir l'erreur du compilateur . La référence au type générique "mmBusinessObjectGeneric" nécessite des arguments dans ( Figure 10 ).

Figure 10 - L'erreur de compilation de ABusinessObject

Maintenant que mmBusinessObjectGeneric est déclaré comme une classe générique, nous devons inclure un argument de type entre parenthèses chaque fois que nous y faisons référence.

  1. Puisque mmBusinessObjectGeneric ne fonctionne qu'avec des instances de NSManagedObject ou de ses sous-classes, modifiez la référence à mmBusinessObjectGeneric :

  1. Appuyez sur Command+B pour construire le projet. Cela génère une nouvelle erreur de compilation, Les classes dérivées de classes génériques doivent également être génériques ( Figure 11 ).

Figure 11 - Les sous-classes des classes génériques doivent également être génériques !

C'est une autre exigence des génériques dans Swift. Cela signifie que nous devons également déclarer ABusinessObject comme une classe générique.

  1. Ajoutez la déclaration générique suivante pour faire de ABusinessObject une classe générique :

Puisque mmBusinessObjectGeneric ne fonctionne qu'avec des instances de NSManagedObject , nous devons déclarer ABusinessObject pour faire de même.

  1. Au lieu de répéter NSManagedObject deux fois dans la déclaration de classe, changez le type de la référence mmBusinessObjectGeneric en T :

  1. Appuyez à nouveau sur Command+B et vous pouvez voir que l'erreur s'est déplacée plus loin dans la chaîne d'héritage jusqu'aux classes Personne et Société .
  1. Sélectionnez Person.swift dans le navigateur de projet et ajoutez les déclarations génériques suivantes :

Ceci déclare Person comme une classe générique avec T comme un paramètre de remplacement qui ne fonctionne qu'avec les instances de la classe PersonEntity . Le caractère de remplacement T est utilisé comme argument de type pour la superclasse ABusinessObject .

  1. Sélectionnez Company.swift dans le navigateur de projet et ajoutez les déclarations génériques suivantes :

Ceci déclare Company comme une classe générique qui utilise T comme paramètre de remplacement, et ne fonctionne qu'avec des instances de la classe CompanyEntity . Le Tplaceholder est utilisé comme argument de type pour la superclasse ABusinessObject .

dépeint la nouvelle hiérarchie générique des classes.

Figure 12 - La nouvelle hiérarchie des classes

  1. Appuyez sur Command+B et il n'y a plus d'erreurs de compilation !

Vous êtes maintenant prêt à faire l'essai de ces classes génériques !

Tester la classe générique

  1. Sélectionnez le fichier ViewController.swift dans le navigateur de projet.
  1. Supprimez les types de variables explicites du code qui appelle la méthode createEntity sur l'objet Person :

Cela n'est plus nécessaire puisque l'objet Person fait effectivement passer le type PersonEntity par la chaîne d'héritage où il est utilisé dans la méthode createEntity . Swift peut maintenant déduire le type de valeur de retour comme PersonEntity .

  1. Faites de même pour le code qui appelle createEntity sur l'objet Company :

  1. Dans la méthode viewDidLoad , cliquez sur la variable personEntity . L'inspecteur de l'aide rapide sur le côté droit de la fenêtre Xcode indique qu'elle est de type PersonEntity ( Figure 13 ).

Figure 13 - personEntity est de type PersonEntity .

  1. Dans la méthode viewDidLoad , cliquez sur la variable companyEntity . L'inspecteur de l'aide rapide indique que la variable est du type CompanyEntity ( Figure 14 ).

Figure 14 - companyEntity est de type CompanyEntity .

  1. Ajoutez le code suivant à la méthode viewDidLoad pour prouver que la méthode createEntity renvoie des entités du type correct :

  1. Le point d'arrêt étant toujours défini sur la ligne de code qui crée une nouvelle PersonEntity ( Figure 7 ), cliquez sur le bouton Run de Xcode . Lorsque le point d'arrêt est atteint, cliquez sur Step Into dans la barre d'outils Debug pour passer à la méthode createEntity sur l'objet Person . La vue des variables montre que le caractère de remplacement T est du type PersonEntity , comme il devrait l'être ( Figure 15 ) !

Figure 15 - Le caractère de remplacement T est de type PersonEntity .

  1. Cliquez sur Step Out pour revenir à la méthode viewDidLoad , puis cliquez quatre fois sur Step Over pour exécuter le code qui affiche les valeurs des propriétés dans la console :

Nom : Kevin McNeish

  1. Cliquez deux fois sur Step Over, puis une fois sur Step In pour exécuter la méthode createEntity sur l'objet Company . La vue des variables montre que le caractère de remplacement T est maintenant du type CompanyEntity ( Figure 16 ).

Figure 16 - Le caractère de remplacement T est de type CompanyEntity .

  1. Cliquez sur "Step Out", puis cliquez quatre fois sur "Step Over" pour exécuter le code qui affiche les valeurs des propriétés dans la console :

Société : Pomme - www.apple.com

  1. Cliquez sur le bouton Stop de Xcode .

Nous avons créé une classe générique dont les méthodes peuvent renvoyer le type que nous spécifions !

Mise en œuvre d'autres méthodes génériques

Une fois que vous avez établi la hiérarchie d'héritage des classes génériques, il est facile de mettre en œuvre d'autres méthodes génériques. Essayons-le.

  1. Sélectionnez le fichier ViewController.swift dans le navigateur de projet.
  1. Cliquez sur les variables personList et companyList et notez que l'inspecteur de l'aide rapide indique qu'elles font partie du Array ? .
  1. Sélectionnez le fichier mmBusinessObjectGeneric.swift dans le navigateur de projet. Changez le type de retour de la méthode getAllEntities et la dernière ligne de code comme opération en Array ? comme indiqué ici :

  1. Sélectionnez le fichier ViewController.swift dans le navigateur de projet. Cliquez sur les variables personList et companyList et vous pouvez voir que leurs types sont maintenant Array ? et Array ? comme elles devraient l'être !
  1. N'hésitez pas à relancer le projet et à consulter les tableaux personList et companyList de première main.

Conclusion

Les génériques sont le cadeau qui continue à donner, et offre encore plus d'avantages pour les méthodes héritées d'une classe générique. Ils vous permettent de créer des classes génériques personnalisées que vous pouvez utiliser dans tous vos projets sans la tâche fastidieuse d'une rétrogradation constante !

Click to rate this post!
[Total: 0 Average: 0]

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *