• Aucun résultat trouvé

Notre solution pour l’évaluation dynamique des messages

Partie III : Une architecture de mise en œuvre 103

9.2 Notre solution pour l’évaluation dynamique des messages

Notre solution consiste à encapsuler chaque méthode publique d’un objet interagissant dans un objet permettant l’invocation de cette méthode depuis les autres objets du système. Afin de respecter le typage fort imposée par le langage cible, une réification des paramètres est également mise en place. C’est cette représentation des paramètres qui est passée à l’objet encapsulant la méthode.

9.2.1 Réification des méthodes

Cette solution permet à un objet d’exécuter la méthode encapsulée sans rien connaître à priori sur cette méthode. La mise en œuvre de cette solution est basée sur le schéma de conception Commande (Command Design Pattern [GHJ+

95, pp. 233–242]) et présentée par la figure 9.1. Ainsi, en réifiant la méthode, le mécanisme

method method->Execute () Evaluate () MsgReactiveBehavior InteractingRule Execute () ReifiedMethod MetaObject Object

FIG. 9.1 –Instanciation du schéma de conception Commande dans le cadre de la réifica-tion des méthodes

d’exécution des comportements réactifs des inter-actions peut invoquer l’exécution de cette méthode puisque cette invocation est elle-même un objet (l’objet encapsulant la méthode) : « The Command pattern let objects make requests of unspecified application objects by turning the request itself into an object. » [GHJ+

95, p. 233].

En effet, la connaissance requise pour exécuter la méthode (et en particulier la façon de désembal-ler les paramètres réifiés) est entièrement conte-nue dans l’objet réifiant cette dernière. De ce fait, le mécanisme d’exécution du comportement

réac-tif d’envoi de messages n’a pas besoin de disposer de la connaissance nécessaire à l’exécution de la méthode.

9.2.2 Réification des paramètres

Le mécanisme d’encapsulation que nous proposons est proche de celui défini dans la première ver-sion d’Open C++ [Chi93] et consiste en une réification des paramètres du message. En fait les para-mètres du message contrôlé sont « empaquetés » (packaged) et regroupés dans un objet conteneur (une

liste). Ces paramètres peuvent ainsi être récupérés à partir de cette liste et « désempaquetés » (unpa-ckaged) avant d’être utilisés. L’objet conteneur peut être considéré comme le bloc de pile de l’envoi du message.

polymorphisme encapsulation

types des paramètres des

messages contrôlés



réification

des

param

ètres

9.2a – Mécanisme d’encapsulation des types des paramètres

display (int, Caneva) aPoint y display x 1 2 métaobjet aCaneva ... ... scale 1.5 3 réification du messageréification du messageréification du message reifiedMessage name object display args aListOfArgs arg2 arg1 reifiedInteger value 3 objet envoi de message référence

9.2b – Réification des messages

FIG. 9.2 –Réification des paramètres

Notre mécanisme de réification des paramètres est basé sur les concepts d’encapsulation et de poly-morphisme du modèle à objets sous-jacent. En effet, l’encapsulation va réaliser la réification proprement dites, tandis que le polymorphisme va rendre tous les paramètres réifiés compatibles vis-à-vis du typage sans toutefois perdre le typage d’origine des paramètres. Ce typage est en effet nécessaire lors de l’opé-ration de déification1.

Le mécanisme d’encapsulation est réalisé par application du schéma de conception (design pattern) nommé Stratégie (Strategy Design Pattern [GHJ+

95, pp. 315–323]), tandis que le polymorphisme est basé sur le concept d’héritage de classes du modèle à objets (figure 9.2a). La figure 9.2b présente notre mécanisme de réification des messages. Elle montre notamment l’utilisation qui est faite du mécanisme d’encapsulation des paramètres et la manière dont les informations constituant le message sont réifiées. Afin que le support des interactions soit transparent du point de vue du programmeur, notre méca-nisme d’encapsulation des paramètres des messages contrôlés doit lui aussi être transparent vis-à-vis du programmeur. Ceci implique que l’architecture du support des interactions doive mettre en œuvre des mécanismes d’emballage et de désemballage pour chacun des types des paramètres des messages susceptibles d’être contrôlés (en effet un message devient contrôlé lorsqu’un comportement réactif lui est associé).

La définition de ces mécanismes a forcément lieu lors de la phase de compilation [Ber99] et nécessite l’utilisation d’un compilateur « ouvert » (pré-compilateur ou méta-compilateur). En effet, l’instanciation de la classe encapsulant un type donné connu uniquement lors de l’exécution requiert la génération du code décrivant l’emballage et le désemballage pour ce type (le code de la classe). Cependant, cette classe à instancier n’est réellement connue que lors de l’exécution. Ainsi, le mécanisme d’instanciation standard tel que défini dans le modèle à objets ne permet pas de réaliser l’instanciation d’une classe non connue à la compilation. Heureusement, le schéma de conception nommé Constructeur Virtuel (Virtual Constructor Design Pattern [GHJ+

95, pp. 107–116]) permet de résoudre ce problème.

Array

Content : Value GetType () : string GetValue () : Value& SetValue (val : Value&)

(type : string)  SetType tt ReifyTypeRef GetType () : stringt Reify Content : Value GetType () : string GetValue () : Value SetValue (val : Value)

(type : string) SetType tt ReifyType Content : Value GetType () : string GetValue () : Value SetValue (val : Value )

(type : string)    SetType tt ReifyTypePtr

Value : class T Value : class T Value : class T Parameters

Array : Reify [ ] operator[ int x] : Reify tt

return Array [x]

FIG. 9.3 –Schéma UML des classes réifiant les paramètres

La figure 9.3 montre, sur un schéma de classes UML [BJR97], « l’instanciation » du schéma de conception Stratégie pour la réification des paramètres. On peut notamment voir que le mécanisme de réification est le même pour chaque style de types (« types simples », « types référencés » et « types pointés »). De ce fait nous avons utilisé le concept de classes génériques (template classes) et les avons paramétrées par le type réifié.

EXEMPLE. — CODE D’UNE MÉTHODE RÉIFIÉE

Nous présentons ci-dessous le code de la classe correspondante à l’entitéReifiedMethodde la figure 9.1 pour une méthode dont la signature estaReturnType aClass::aMethod (aFirstType x, aSecondType * y).

On peut noter que l’on passe notamment comme paramètre à la méthodeexecutel’objet ainsi que la signature de la fonction ayant déclenchée l’interaction impliquant cette évaluation dynamique d’un message. En effet, si le message à exécuter est le message déclenchant alors il faut invoquer la méthode non contrôlée (son nom est suffixée avec_original). Dans tous les autres cas c’est la méthode contrôlée qui doit être est invoquée.

1: class aClass_aMethod_aFirstType_aSecondTypePtr : public __FunctionCall 2: {

3: public:

4: void execute (Reify *anObject, Parameters &params, Reify **ret, const char *pFctName, Reify *pObject); 5: {

6: aReturnType vResult;

7: aClass * pObjectCall = ((ReifyPtr <aClass> *)anObject)->GetValue (); 8:

9: if (pObjectCall == ((ReifyPtr <aClass> *)pObject)->GetValue () && !strcmp (pFctName, "aMethod_aFirstType_aSecondTypePtr")) 10: vResult = pOjectCall->aMethod_original (((ReifyType <aFirstType> *)(params [0]))->GetValue (),

11: vResult = pObjectCall->aMethodoriginal (((ReifyTypePtr <aSecondType> *)(params [1]))->GetValue ());

12: else

13: vResult = pObjectCall->aMethod (((ReifyType <aFirstType> *)(params [0]))->GetValue (),

14: vResult = pObjectCall->aMethod (((ReifyTypePtr <aSecondType> *)(params [1]))->GetValue ());

15:

16: if (ret)

17: *ret = new ReifyType <aReturnType> (vResult); 18: else if (params->GetSize () == 2)

19: ((ReifyType <aReturnType> *)(params [2]))->SetValue (vResult); 20: }

21: };

9.2.3 Détermination de la méthode réifiée

Réifier toutes les méthodes publiques de tous les objets interagissants n’est pas suffisant pour pouvoir évaluer dynamiquement cette méthode. En effet, encore faut il pouvoir déterminer l’objet encapsulant la bonne méthode. Puisque la classe à instancier qui réifie la bonne méthode est spécifique à une règle d’interaction, le mécanisme d’exécution des comportements réactifs ne peut prédire à la compilation quelle classe précise doit être instanciée — le méca-nisme sait juste quand instancier la classe réifiant une méthode, mais pas quelle classe doit être instanciée.

Il est donc nécessaire d’extraire du mécanisme

method2 method1 method3 anObject method2 method3 method1 var val Execute aMetaObject ... reified objet référence liste ou hashtable aHashTable meth2 meth3 meth1 ... aReifiedMethod réification de la méthoderéification de la méthoderéification de la méthode

FIG. 9.4 –Enregistrement des instances des mé-thodes réifiées

d’exécution des comportements réactifs la manière d’instancier la classe et de placer cette information ailleurs. Ceci peut être réalisé grâce à l’utilisation du schéma de conception de Constructeur Virtuel [GHJ+

95, pp. 107–116].

L’utilisation de ce mécanisme d’instanciation par « constructeur virtuel » implique également d’enre-gistrer une instance de chacune de ces classes avant l’utilisation du mécanisme d’évaluation dynamique des messages afin de pouvoir associer au nom de la méthode à exécuter l’objet la réifiant (figure 9.4). Cet enregistrement peut être basée sur l’utilisation du schéma de conception Poids Mouche (Flyweight Design Pattern [GHJ+

9.2.4 Mise en place de la solution

Afin que le support des interactions soit transparent du point de vue du programmeur, la réification des méthodes doit être entièrement à la charge de notre architecture. Or cette réification des méthodes implique la génération du code décrivant, pour chacune des méthodes à réifier, l’instanciation du schéma de conception Commande (cf. l’exemple montrant le code d’une méthode réifiée).

En effet, ce code, écrit dans le langage applicatif, ne peut pas être générique puisque seul l’objet « Commande » dispose de la connaissance requise pour exécuter la méthode et effectuer les vérifications de typage. La génération de ce code a nécessairement lieu lors de la phase de compilation. De plus, elle nécessite l’utilisation d’un compilateur « ouvert » (ce compilateur pouvant être le même que celui utilisé pour la réification des types des paramètres). Dans notre cas, ce compilateur est Open C++ v2 [Chi95]. Il est à noter que le mécanisme d’évaluation dynamique des messages fait partie intégrante de la métaclasseMetaObjectdéfinie dans le chapitre précédent (puisque c’est elle qui a la charge de l’exécution des comportements réactifs). On peut remarquer que cette métaclasse n’existe pas en tant que tel lors de l’exécution du programme.

Sa mise en œuvre se compose, en effet, d’un ensemble de classes dont certaines sont générées lors de la compilation (réification des méthodes et des types des paramètres). En fait, nous utilisons le MOP à la compilation d’Open C++ v2 pour définir notre propre MOP à l’exécution (ce dernier étant spécialisé dans le support des interactions).

9.2.5 Extension de la solution aux évaluations dynamiques de messages

distants

Il peut être nécessaire d’évaluer dynamiquement un message dont l’objet receveur (le destinataire) est situé sur un site distant de celui de l’objet émetteur (celui qui a déclenché l’interaction).

Si l’objet destinataire n’est pas un objet distribué, alors l’évaluation dynamique du message doit être réalisée sur le site de l’objet destinataire. Ceci implique de déléguer l’évaluation du message au méca-nisme d’évaluation dynamique des messages du site contenant l’objet destinataire. Ainsi, le mécaméca-nisme d’évaluation dynamique des messages doit être accessible à distance (sur le bus logiciel).

Pour ce faire, nous définissons un serveur de requêtes dont le rôle est de traiter les demandes d’éva-luation dynamique des messages provenant des autres sites (figure 9.5). L’exécution des messages trans-mis au serveur de requêtes fait appel au mécanisme d’évaluation dynamique des messages présent en local sur le site. Ainsi, les requêtes contiennent, d’une part, l’ensemble des informations nécessaires pour déterminer sans ambiguïté la méthode à exécuter que et, d’autre part, la liste des arguments à transmettre lors de l’appel de la méthode. Une fois ces éléments obtenus par le serveur de requêtes, le message est exécuté comme cela a été décrit ci-dessus.

Bus Réseau Application Site 1 Application Site 3 Application Site 2 Serveur de requêtes Objet

9.5a – Vue d’ensemble du serveur de requêtes

Bus Réseau Serveur de

requêtes Objet Requête Exécution Évaluation dynamique « virtuelle »

9.5b – Cheminement d’une requête FIG. 9.5 –Le serveur de requêtes

Si l’objet destinataire est un objet distribué (un objet CORBA dans notre cas) le mécanisme d’in-vocation dynamique (DII) peut être utilisé pour réaliser cette évaluation du message. Ainsi, le site où se trouve l’objet émetteur du message (celui qui a déclenché l’interaction) construit une requête DII CORBA et l’envoit au bus logiciel. Par conséquent, cette solution évite d’avoir recours au serveur de requêtes que nous venons de définir et, de ce fait, à réifier l’exécution du message aussi bien du côté émetteur que du côté récepteur.

Il est à noter qu’aussi bien le serveur de requêtes que le mécanisme DII contribuent activement au fait que l’exécution des interactions est entièrement transparente du point du programmeur.