Modèles de conception délégation

Le modèle de délégation est l'un des modèles les plus courants dans les développements iOS et OS X. Il s’agit d’un modèle simple, très utilisé par les frameworks d’Apple. Même la plus simple des applications iOS utilise la délégation pour faire son travail. Commençons par examiner la définition de la délégation.

1. Qu'est-ce que la délégation??

Définition

La définition du modèle de délégation est courte et simple. Voici comment Apple définit le modèle.

Un délégué est un objet qui agit pour le compte d'un autre objet ou en coordination avec un autre objet lorsque cet objet rencontre un événement dans un programme..

Décomposons cela. Le modèle de délégation implique deux objets, le délégué et l'objet délégué. le UITableView classe, par exemple, définit un déléguer propriété à laquelle il délègue des événements. La propriété delegate doit être conforme à la UITableViewDelegate protocole, qui est défini dans le fichier d’en-tête du UITableView classe.

Dans cet exemple, l'instance de vue de table est l'objet délégué. Le délégué est généralement un contrôleur de vue, mais il peut s’agir de tout objet conforme à la UITableViewDelegate protocole. Si vous ne connaissez pas bien les protocoles, une classe est conforme à un protocole si elle implémente les méthodes requises du protocole. Nous allons regarder un exemple un peu plus tard.

Lorsque l'utilisateur appuie sur une ligne de la vue tableau, celle-ci en informe son délégué en lui envoyant un message tableView (_: didSelectRowAtIndexPath :). Le premier argument de cette méthode est la vue table envoyant le message. Le deuxième argument est le chemin d'index de la ligne que l'utilisateur a tapée.

La vue de table avertit uniquement son délégué de cet événement. Il appartient au délégué de décider de ce qui doit se passer lorsqu'un tel événement se produit. Comme vous le constaterez tout à l'heure, cette séparation des responsabilités est l'un des principaux avantages du modèle de délégation..

Avantages

Réutilisabilité

La délégation présente plusieurs avantages, le premier étant la réutilisabilité. Étant donné que la vue de table délègue une interaction utilisateur à son délégué, la vue de tableau n'a pas besoin de savoir ce qui doit se passer lorsque l'une de ses lignes est exploitée..

En d'autres termes, la vue tabulaire peut rester ignorante des détails d'implémentation de la manière dont l'interaction utilisateur est gérée par l'application. Cette responsabilité est déléguée au délégué, un contrôleur de vue par exemple.

L'avantage direct est que le UITableView La classe peut être utilisée telle quelle dans la plupart des situations. La plupart du temps, il n'y a pas besoin de sous-classe UITableView l'adapter aux besoins de votre application.

Couplage lâche

Un autre avantage important de la délégation est le couplage lâche. Dans mon article sur les singletons, je souligne que le couplage étroit doit être évité autant que possible. La délégation est un modèle de conception qui favorise activement le couplage lâche. Qu'est-ce que je veux dire par là?

le UITableView la classe est couplée à son délégué pour faire son travail. Si aucun délégué n'est associé à la vue table, celle-ci ne peut pas gérer ou répondre aux interactions de l'utilisateur. Cela signifie qu'il doit y avoir un certain niveau de couplage. La vue tabulaire et son délégué sont toutefois faiblement couplés, car chaque classe qui implémente le UITableViewDelegate Le protocole peut agir en tant que délégué de la vue de table. Le résultat est un graphe d'objet souple et faiblement couplé.

Séparation des responsabilités

Un avantage moins connu de la délégation est la séparation des responsabilités. Chaque fois que vous créez un graphe d'objets, il est important de savoir quels objets sont responsables de quelles tâches. Le modèle de délégation rend cela très clair.

Dans le cas du UITableView classe, le délégué de la vue table est responsable de la gestion des interactions utilisateur. La vue table elle-même est responsable de la détection de l’interaction de l’utilisateur. C'est une séparation claire des responsabilités. Une telle séparation rend votre travail de développeur beaucoup plus facile et plus clair..

2. exemple

Il existe quelques variantes du modèle de délégation. Continuons en explorant plus avant la UITableViewDelegate protocole.

Délégation

le UITableViewDelegate Le protocole doit être implémenté par le délégué de la vue de table. La vue table informe son délégué par le biais du UITableViewDelegate protocole sur l'interaction utilisateur, mais il utilise également le délégué pour sa mise en page.

Une différence importante entre Swift et Objective-C réside dans la possibilité de marquer les méthodes de protocole comme facultatives. En Objective-C, les méthodes d'un protocole sont requises par défaut. Les méthodes du UITableViewDelegate protocole, cependant, sont facultatifs. En d’autres termes, il est possible pour une classe de se conformer à la UITableViewDelegate protocole sans implémenter aucune des méthodes du protocole.

Dans Swift, cependant, une classe conforme à un protocole particulier est requise pour implémenter chaque méthode définie par le protocole. Ceci est beaucoup plus sûr car l'objet délégué n'est pas obligé de vérifier si le délégué implémente une méthode de protocole. Cette différence subtile, mais importante, est illustrée plus loin dans ce tutoriel lorsque nous implémentons le modèle de délégation..

La source de données

Il existe un autre modèle étroitement lié au modèle de délégation, le modèle de source de données. le UITableViewDataSource Le protocole est un exemple de ce modèle. le UITableView la classe expose un la source de données propriété qui est de type UITableViewDataSource (identifiant en Objective-C). Cela signifie que la source de données de la vue table peut être n’importe quel objet qui implémente la UITableViewDataSource protocole.

L'objet source de données est responsable de la gestion de la source de données de l'objet dont il est la source de données. Il est important de noter que l'objet de source de données est responsable de la conservation d'une référence aux éléments qu'il expose à l'objet cible, telle qu'une vue de table ou une vue de collection..

Une vue sous forme de tableau, par exemple, demande à sa source de données les données à afficher. La vue tabulaire n'est pas responsable de la conservation des objets de données à afficher. Ce rôle est transféré à l'objet source de données.

Le modèle de source de données s’intègre parfaitement dans la Modèle Vue Contrôleur ou MVC modèle. Pourquoi donc? Une vue de tableau, par exemple, fait partie du calque de vue. Il ne sait pas et ne devrait pas connaître la couche modèle et n'est pas en charge de la gestion des données provenant de la couche modèle. Cela implique que la source de données d'une vue tabulaire, ou de tout autre composant de vue implémentant le modèle de source de données, est souvent un contrôleur quelconque. Sur iOS, c'est généralement un UIViewController sous-classe.

Les signatures de méthode d'un protocole de source de données suivent le même modèle que celles d'un protocole de délégué. L'objet envoyant les messages à la source de données est transmis en tant que premier argument. Le protocole de source de données ne doit définir que des méthodes liées aux données utilisées par l'objet demandeur.

Une vue sous forme de tableau, par exemple, demande à sa source de données le nombre de sections et de lignes à afficher. Mais il indique également à la source de données qu'une ligne ou une section a été insérée ou supprimée. Ce dernier point est important car la source de données doit se mettre à jour pour refléter les modifications visibles dans la vue tableau. Si la vue tabulaire et la source de données ne sont plus synchronisées, de mauvaises choses se produisent.

3. mise en œuvre

Objectif c

L'implémentation du modèle de délégué est assez simple maintenant que nous comprenons son fonctionnement. Jetez un oeil à l'exemple Objective-C suivant.

#importation  @protocol AddItemViewControllerDelegate; @interface AddItemViewController: UIViewController @property (faible, non atomique) id déléguer; @end @protocol AddItemViewControllerDelegate  - (void) viewControllerDidCancel: (AddItemViewController *) viewController; - (void) viewController: (AddItemViewController *) viewController didAddItem: (NSString *) item; @optional - (BOOL) viewController: (AddItemViewController *) viewController validateItem: (NSString *) item; @fin

Nous déclarons une classe, AddItemViewController, qui s'étend UIViewController. La classe déclare une propriété, déléguer, de type identifiant. Notez que la propriété est marquée comme faible, ce qui signifie qu'un AddItemViewController l'instance conserve une faible référence à son délégué.

Notez également que j'ai ajouté une déclaration de protocole à terme sous la déclaration d'importation du framework UIKit. Ceci est nécessaire pour éviter un avertissement du compilateur. Nous pourrions déplacer la déclaration de protocole sous l'instruction d'importation, mais je préfère la placer sous l'interface de classe. Ce n'est rien de plus qu'une préférence personnelle.

La déclaration de protocole est également assez simple. le AddItemViewControllerDelegate protocole étend la NSObject protocole. Ce n'est pas obligatoire, mais cela s'avérera très utile. Nous verrons pourquoi c'est un peu plus tard.

le AddItemViewControllerDelegate Le protocole déclare deux méthodes obligatoires et une méthode facultative. Comme je l'ai mentionné plus tôt, il est recommandé de passer l'objet délégué en tant que premier paramètre de chaque méthode déléguée pour indiquer au délégué quel objet envoie le message..

Les méthodes requises informent le délégué d'un événement, d'une annulation ou d'un ajout. La méthode facultative demande des commentaires au délégué. Il s'attend à ce que le délégué revienne OUI ou NON.

C'est la première pièce du puzzle de la délégation. Nous avons déclaré une classe qui déclare déléguer propriété et nous avons déclaré un protocole de délégué. La seconde pièce du puzzle consiste à invoquer les méthodes de délégué dans AddItemViewController classe. Voyons comment cela fonctionne.

Dans la mise en œuvre de la AddItemViewController classe, nous mettons en œuvre un Annuler: action. Cette action pourrait être reliée à un bouton de l'interface utilisateur. Si l'utilisateur appuie sur le bouton, le délégué est informé de cet événement et, par conséquent, le délégué peut ignorer le message. AddItemViewController exemple.

- (IBAction) cancel: (id) expéditeur if (self.delegate && [self.delegate respondsToSelector: @selector (viewControllerDidCancel :)]) self.delegate viewControllerDidCancel: self]; 

Il est recommandé de vérifier que l'objet délégué n'est pas néant et qu'il implémente la méthode déléguée que nous sommes sur le point d'appeler, viewControllerDidCancel:. C’est facile grâce au répond au sélecteur: méthode, déclarée dans le NSObject protocole. C’est la raison pour laquelle le AddItemViewControllerDelegate protocole étend la NSObject protocole. En étendant la NSObject protocole, nous obtenons cette fonctionnalité gratuitement.

Vous pouvez omettre le contrôle de la propriété déléguée en cours de néant, puisque répond au sélecteur: reviendra néant si la propriété delegate est néant. J'ajoute habituellement ce chèque car il montre clairement ce que nous testons.

La troisième et dernière pièce du puzzle est la mise en œuvre du protocole de délégué par l'objet délégué. L'extrait de code suivant montre la création d'un AddItemViewController instance et la mise en œuvre de l’une des méthodes de délégation.

- (IBAction) addItem: (id) expéditeur // initialise View Controller AddItemViewController * viewController = [[AddItemViewController alloc] init]; // Configurer le contrôleur de vue [viewController setDelegate: self]; // Contrôleur de vue présent [self presentViewController: viewController animated: YES achèvement: nil]; 
- (void) viewControllerDidCancel: (AddItemViewController *) viewController // Annulation de l'ajout du contrôleur de vue d'élément…

N'oubliez pas de conformer la classe qui agit en tant que délégué à la AddItemViewControllerDelegate protocole comme indiqué ci-dessous. Vous pouvez ajouter ceci dans l'interface de classe ou dans une extension de classe privée..

#import "AddItemViewController.h" @interface ViewController ()  @fin

Rapide

Dans Swift, le modèle de délégation est tout aussi simple à mettre en œuvre et vous constaterez que Swift rend la délégation légèrement plus élégante. Implémentons l'exemple ci-dessus dans Swift. C'est ce que le AddItemViewController classe ressemble à Swift.

Protocole UIKit importation AddItemViewControllerDelegate: NSObjectProtocol func viewControllerDidCancel (viewController: AddItemViewController) func viewController (viewController: AddItemViewController, didAddItem: String) func viewController (viewController: AddItemViewController, validateItem: String) -> Bool class AddItemViewController: UIViewController délégué var: AddItemViewControllerDelegate? func cancel (expéditeur: AnyObject) delegate? .viewControllerDidCancel (self)

La déclaration de protocole est un peu différente dans Swift. Notez que le AddItemViewControllerDelegate protocole étend la NSObjectProtocol à la place du NSObject protocole. Dans Swift, les classes et les protocoles ne peuvent pas avoir le même nom. C’est pourquoi NSObject le protocole est nommé différemment dans Swift.

le déléguer propriété est une variable de type AddItemViewControllerDelegate?. Notez le point d'interrogation à la fin du nom du protocole. La propriété delegate est facultative.

dans le Annuler(_:) méthode, nous invoquons la viewControllerDidCancel (_ :) méthode déléguée. Cette ligne unique montre à quel point Swift peut être élégant. Nous déballons le déléguer propriété avant d'appeler la méthode delegate. Il n'est pas nécessaire de vérifier si le délégué met en œuvre le viewControllerDidCancel (_ :) méthode puisque chaque méthode d'un protocole est requise dans Swift.

Regardons maintenant le ViewController classe, qui implémente le AddItemViewControllerDelegate protocole. L’interface nous montre que le ViewController la classe étend la UIViewController classe et adopte le AddItemViewControllerDelegate protocole.

importez la classe UIKit ViewController: UIViewController, AddItemViewControllerDelegate func addItem (send: AnyObject) // Initialize View Controller laisser , complétion: nil) func viewControllerDidCancel (viewController: AddItemViewController) // Dismiss Ajouter un contrôleur de vue… func viewController (viewController: 

dans le ajouter un item(_:) méthode, nous initialisons une instance du AddItemViewController classe, définir sa déléguer propriété, et le présenter à l'utilisateur. Notez que nous avons implémenté chaque méthode de délégué du AddItemViewControllerDelegate protocole. Si nous ne le faisons pas, le compilateur nous dira que le ViewController la classe n'est pas conforme à la AddItemViewControllerDelegate protocole. Essayez ceci en commentant l'une des méthodes de délégué.

Conclusion

La délégation est un motif que vous rencontrerez fréquemment lors du développement d'applications iOS et OS X. Le cacao repose énormément sur ce modèle, il est donc important de se familiariser avec ce modèle..

Depuis l’introduction des blocs, il y a quelques années, Apple a progressivement proposé une API basée sur des blocs alternatifs à certaines implémentations de délégation. Certains développeurs ont suivi l'exemple d'Apple en proposant leurs propres alternatives basées sur des blocs. La bibliothèque AFNetworking, par exemple, repose largement sur des blocs plutôt que sur la délégation, ce qui se traduit par une API élégante et intuitive..