Modèle de conception : La délégation dans Cocoa. Principe et mise en pratique.

Image non disponible

Cocoa s'appuie sur le langage Objective-C qui, contrairement au C++, ne propose pas un modèle de classe avec héritage multiple. L'héritage simple est la règle, comme dans le monde Java.

Si le langage propose des solutions techniques pour contourner les limites de l'héritage en introduisant l'idée de protocoles (interfaces en Java), le socle Cocoa s'appuie sur une solution conceptuelle : la délégation.

Ainsi, au lieu de dériver une classe NSApplication ou NSWindow pour l'enrichir de comportement spécifiques à votre application, Cocoa vous propose d'implémenter les comportements spécifiques dans un objet délégué.

Article lu   fois.

L'auteur

Site personnel

Liens sociaux

Viadeo Twitter Google Bookmarks ! Facebook Digg del.icio.us Yahoo MyWeb Blinklist Netvouz Reddit Simpy StumbleUpon Bookmarks Share on Google+ 

I. Principe de la délégation

Certaines classes du socle Cocoa permettent de définir un délégué pour la gestion des comportements particuliers.
Ce modèle de conception est défini comme la délégation.

Ce délégué est une propriété de la classe qui est nommé delegate. Les méthodes associées suivent les conventions classiques.

Accesseur - (id) delegate ;
Modificateur - (void) setDelegate : (id) anObject;

Le fonctionnement de la délégation est simple.

  1. Lorsque l'objet reçoit un message, il vérifie que son délégué ne propose pas une implémentation spécifique. Si c'est le cas, le message est transféré au délégué qui prend en charge son traitement.
  2. Si le délégué ne prend pas en charge le message, l'objet utilise son implémentation par défaut pour la gestion du message.

Chaque classe qui implémente le modèle de délégation précise dans sa documentation la liste des méthodes qu'un délégué peut implémenter.

C'est toute la différence avec l'implémentation d'une interface en Java. Le délégué ne doit pas obligatoirement implémenter toutes les méthodes d'un protocole. Il se contente de proposer une implémentation pour les méthodes qu'il souhaite spécialiser. On dispose ainsi de la souplesse de l'héritage sans avoir la contrainte imposée par les protocoles formels.

II. Mise en oeuvre d'un délégué

Pour les classes graphiques, la mise en oeuvre d'un délégué est simple.

Il faut commencer par définir une classe qui va proposer des implémentations spécifiques de méthodes. Cette classe est ensuite liée graphiquement dans Interface Builder. Cette partie vous guidera pas à pas.

Pour commencer créez un projet Cocoa de type « Cocoa Application ».

Une fois le projet créé, ajoutez une nouvelle classe que nous pouvons appeler AppDelegate. Cette classe va pouvoir être utilisée comme délégué de la fenêtre principale de notre application ainsi que comme délégué de l'application elle même.

Vérifiez que tout va bien en compilant le projet.

Passez maintenant dans Interface Builder en ouvrant le fichier NIB de l'application.

Rajoutez une instance de votre classe dans l'application en faisant glisser un NSObject de la palette Library vers la fenêtre du document NIB. Renommez-le en delegate.

Image non disponible

Ouvrez l'inspecteur pour définir la classe de votre instance et utiliser le nom de votre classe déléguée : AppDelegate.

Image non disponible

Nous avons maintenant une instance de notre classe de délégation qui sera créée au démarrage de l'application. Vous allez définir le lien de délégation de l'objet application vers votre objet delegate.

Sélectionnez l'objet Application et affichez les connections dans l'inspecteur. Tirez maintenant un lien de l'attribut delegate vers l'objet delegate situé dans le NIB.

Image non disponible

L'inspecteur affiche maintenant votre objet delegate comme valeur de l'attribut delegate de l'application.

Image non disponible

Faites de même pour la fenêtre de votre application.

Image non disponible

Vous pouvez maintenant quitter Interface Builder et revenir dans XCode pour ajouter les méthodes suivantes dans l'interface de notre classe :

  • Délégation pour l'application applicationWillFinishLaunching appellé au moment où l'application va effectivement démarrer.applicationShouldTerminateAfterLastWindowClosed qui permet de définir si l'application doit quitter lorsque sa dernière fenêtre est fermée.
  • Délégation pour la fenêtre windowWillClose est appelé juste avant la fermeture d'une fenêtre.

Les signatures de messages à intégrer dans le fichier en-tête de votre classe :

 
Sélectionnez
@interface AppDelegate : NSObject {
 // Pas d'attributs pour l'instant
}

// NSWindows delegation

- (void) windowWillClose: (NSNotification *) notification;

// NSApplication delegation

- (void) applicationWillFinishLaunching:
       (NSNotification *) aNotification;

- (BOOL) applicationShouldTerminateAfterLastWindowClosed:
       (NSApplication *) theApplication;

@end

Une fois l'interface définiz il vous suffit de basculer dans l'implémentation de la classe pour ajouter le corps des méthodes.

 
Sélectionnez
@implementation AppDelegate

- (void) windowWillClose: (NSNotification *) notification
{
   NSLog(@"Yep, delegate was notified that window will be closed. Quitting application.");
}

- (void) applicationWillFinishLaunching: (NSNotification *) aNotification
{
   NSLog(@"Application will be launched now.");
}

- (BOOL) applicationShouldTerminateAfterLastWindowClosed:
       (NSApplication *) theApplication
{
   NSLog(@"applicationShouldTerminateAfterLastWindowClosed ?");

   return YES;
}

@end

Compiler et exécutez votre application. En affichant la console vous devriez voir les messages apparaître.

Vous pouvez maintenant jouer un peu en débranchant votre classe soit de l'application, soit de la fenêtre pour apprécier la différence de comportement.

Le projet complet est joint à cet article. Vous pouvez le télécharger pour tester rapidement.

Image non disponible
Project SimpleDelegate : Le code source de l'exemple sous la forme d'un projet XCode 3.

III. Perspective avec Java/Swing

Cocoa met en avant le modèle de la délégation.

Du coté Java/Swing on s'appuie d'avantage sur un modèle plus évènementiel qui utilise les écoutes (listener) sur évènements.

  • La classe peut se voir ajouter autant de listeners que nécessaire.
  • chaque objet en écoute est spécialisé sur un type d'évènements particuliers
  • les listeners sont décrits par des interfaces (comprendre protocoles) et une implémentation doit être fournie.
  • afin de limiter le nombre de méthodes à implémenter dans le code spécialisé, une classe abstraite d'adaptation fournit une implémentation vide pour les méthodes de l'interface.

On se retrouve ainsi pour un type d'évènements donnés avec :

Type d'évènement Xxx
Description d'évènement XxxEvent
Interface d'écoute interface XxxListener
Adapteur class XxxAdapter

Certes ce modèle permet le typage statique des données au prix d'une relative lourdeur conceptuelle puisque pour gérer un évènement on doit au minimum dériver la classe d'adaptation ou implémenter l'interface d'écoute.

Le nombre d'objets mis en oeuvre n'est pas forcément différent, mais la facilité d'écriture n'est pas la même.

Preuve en est, à la suite de l'introduction de Swing, Java s'est vu enrichi d'un moyen de déclarer en-ligne une classe anonyme. Ce n'est peut-être pas le meilleur moyen de structure une application.

IV. Conclusion

Le modèle de la délégation est largement utilisé dans Cocoa pour spécialiser les comportements d'une classe existante.

Ce modèle s'appuie sur le typage dynamique du langage en évitant d'imposer l'utilisation de protocoles formels. Les objets qui spécialisent une classe, ou qui proposent un comportement spécifique, peuvent être ainsi allégés et se concentrer sur l'essentiel.

Le faible couplage entre les objets/vues et les implémentations est aussi un moyen de garantir une bonne réutilisation des composants.