• Aucun résultat trouvé

Génération de l’implémentation orientée objet des nouveaux composants générés (niveau implémentatoire)

Adaptation structurelle par la ré-ingénierie de

3.2 Processus de transformation structurelle de composants existants

3.2.2 Transformation du composant monolithique vers un composant fragmenté

3.2.2.4 Génération de l’implémentation orientée objet des nouveaux composants générés (niveau implémentatoire)

Tout d’abord, les classes d’implémentation de chaque nouveau composant sont générées. Puis, pour chaque classe d’implémentation, le code des méthodes correspondant aux services fournis par le com- posant est transféré de la classe d’implémentation du composant initial vers la nouvelle classe corres- pondante tout en garantissant l’intégrité du code au travers de pré-conditions et de post-conditions (voir Figure 3.9).

Interface_Transfer(c,i,c’) :

Precondition :

is_Class(c)∧ is_Class(c’) ∧ is_Interface_of(c,i) ∧¬is_Interface_of(c’,i) ∧

(∀ m ∈ Method(c) / is_Defined_in(i,m), ¬ is_Method_of(c’,m) )

Postcondition :

(∀ m ∈ Method(c)∪Method(c’) / is_Defined_in(i,m), ¬ is_method_of(c,m) ∧ is_reachable(c,m)) ∧

(∀ m ∈ Method(c)∪Method(c’), is_accessible(m,get_Struct_Entities(m)) ∧

(∀ r ∈ Resource(c) / is_used(m,r), is_coherent(m,r) )

Where :

Boolean is_Class(Class c) : return true iff c is a class.

Boolean is_Interface_of(Class c,Interface i) : return true iff the interface i is implemented by the class c. Boolean is_Defined_in(Interface i, Method m) : return true iff the method m is defined in the interface i. Boolean is_Method_of(Class c, Method m) : return true iff the method m is implemented in the class c. Object[] Method(Class c) : return the list of method implemented in class c.

Object[] get_Struct_Entities(Method m) : return the list of structural-entities used in method m. Boolean is_reachable(Class c, Method m) : return true iff the method m the reachable by the class c.

Boolean is_accessible(Method m, Object[] o) : return true iff all structural-entities contained in o are accessible by the method m. Object[] Resource(Class m) : return the list of resources defined in the class m.

Boolean is_used(Method m,Object r) : return true iff the method m uses the resource r.

Boolean is_coherent(Method m,Object r) : return true iff the resource r state will be coherent when the method m will be invoke.

Figure 3.9 – Génération de l’implémentation du composant

Pour garantir l’intégrité du code généré, chaque service transféré doit pouvoir accéder à toutes les entités structurelles qu’il utilise. Ces entités sont de trois types :

1. les méthodes correspondant à des services de composants

Concernant les méthodes correspondant à des services de composants, celles-ci sont des méthodes dont la signature est contenue dans une interface de composant. Deux cas peuvent être rencontrés lors de l’analyse du code source des méthodes de chaque composant généré : soit le service utilisé est également fourni par le même composant : dans ce cas l’intégrité structurelle est préservée ; soit le service utilisé est fourni par un autre composant : dans ce cas, il est nécessaire de mettre en place une liaison entre les deux composants mis en jeu. Cette opération est réalisée au moment de l’assemblage des composants générés (voir Section 3.2.3.1) ;

2. les méthodes internes

cune interface de composant. Ce sont des méthodes non accessibles de l’extérieur. Pour assurer l’intégrité structurelle et fonctionnelle des composants générés, les composants dont les services utilisent ces méthodes doivent pouvoir y accéder. Pour cela, deux solutions peuvent être envisa- gées :

• la centralisée des méthodes internes

La première solution consiste à produire du code partagé. En fait, ce code sera présent dans une seule implémentation de composant mais devra être accessible pour les autres implémentations de composants le partageant.

La mise en place d’une telle solution pour la gestion des méthodes internes implique la création d’un service associé à chacune de ces méthodes de manière à ce qu’elles soient accessibles à tous les composants qui leur font appel. Ces services pourront être fournis soit par un nouveau composant dédié, soit par un composant existant. De plus, ils doivent être considérés comme des services issus de l’adaptation et donc inaccessibles aux autres composants de l’application. Le choix du composant destiné à fournir un service correspondant à une méthode interne peut être déterminé en fonction des dépendances fonctionnelles. Par exemple, le composant dont les services ou les ressources ont le plus de dépendances fonctionnelles avec la méthode interne va fournir le nouveau service correspondant à celle-ci. Les autres sous-composants pourront alors accéder à cette méthode par l’intermédiaire du service ainsi créé.

Cette solution permet de réduire au minimum la capacité mémoire occupée mais n’est pas op- timale vis-à-vis des temps de réponse relatifs aux services des composants car le code partagé peut se trouver dans l’implémentation d’un composant déployé sur un autre nœud de l’infra- structure distribuée ;

• la duplication des méthodes internes

Contrairement à la technique centralisée, l’approche avec duplication n’engendre pas la création de nouveaux services. En fait, le code de chaque méthode interne est dupliqué dans chaque composant pour lequel au moins un de ses services ou méthodes internes fait appel à cette méthode. Ainsi, tous les services disposeront des méthodes internes dont ils ont besoin pour fonctionner.

Cette solution réduit au minimum la durée des communications entre les composants, étant donné que chaque composant dispose de tous les éléments nécessaires à son fonctionnement. Cependant, cette stratégie n’est pas optimale en ce qui concerne la capacité mémoire utilisée du fait de la duplication de code.

Le choix de la meilleure stratégie pour gérer l’éventuel partage des méthodes internes dépend des objectifs fixés pour l’application à déployer : réduire le temps CPU au minimum ou bien diminuer la mémoire utilisée. Nous avons opté, dans notre approche, pour une stratégie de gestion flexible. En fait, la politique de gestion du partage des entités structurelles est paramétrable à travers une in- terface d’administration. Ainsi, suivant le besoin, les acteurs de l’adaptation peuvent choisir l’une ou l’autre des stratégies ;

3. les ressources logicielles

Comme nous l’avons évoqué précédemment, il existe un type particulier d’entités logicielles qui possèdent un état dont la persistance est supérieure à celle du service dans lequel elle est utilisée : les ressources logicielles. De la même manière que pour les méthodes internes, pour assurer le

maintien de l’intégrité structurelle relative à l’utilisant de ressources logicielles dans le corps de méthode, deux stratégies peuvent être envisagées :

• une gestion centralisée des ressources logicielles

Cette stratégie peut se décliner sous deux formes : soit la mise en place d’un composant spé- cifique exclusivement dédié à la gestion des ressources partagées i.e. toutes les ressources lo- gicielles et les méthodes pour y accéder sont exclusivement définies dans la classe d’implé- mentation associée à ce composant spécifique et tous les nouveaux composants générés doivent s’adresser à lui pour accéder à une ressource partagée ; soit la centralisation des ressources au niveau d’un seul composant généré i.e. la ressource est définie dans la classe d’implémentation d’un seul composant généré et pour y accéder, les autres composants doivent connaître le nom de ce composant ;

• une gestion des ressources logicielles avec duplication

Une autre stratégie consiste à dupliquer les ressources partagées dans chaque classe d’implé- mentation des composants dont les méthodes les utilisent.

Dans tous les cas, le maintien de la cohérence de l’état des ressources partagées est nécessaire. Dans le cadre de notre approche, nous avons privilégié la stratégie de duplication des ressources partagées car elle assure une plus grande autonomie des composants logiciels générés. En effet, par exemple, si l’on opte pour une stratégie centralisée et qu’un composant définissant une ressource partagée devient inaccessible, le comportement des autres composants utilisant cette ressource n’est plus garanti. Alors que s’il contient une copie de la ressource partagée, il pourra continuer à fournir ses services en se basant sur l’état courant de la ressource.

Exemple de l’agenda-partagé

Si l’on se place dans l’exemple de l’agenda partagé, au départ le composant a pour ressource le nombre de jours libres d’une personne.

1 p u b l i c c l a s s A g e n d a P a r t a g é I m p l imp lemen ts Reunion , A bs ence , . . . , A g e n d a P a r t a g é A t t r i b u t e s { 2 . . .

3 p r i v a t e i n t n b _ j o u r s _ l i b r e s = 0 ; 4 . . .

5 }

Étant donné que cette ressource est utilisée par des méthodes des interfaces Réunion et Absence, elle va être dupliquée dans ces composants GestionaireDeRéunion et GestionnaireDAbsences.

1 p u b l i c c l a s s G e s t i o n n a i r e D e A g e n d a I m p l imp lemen ts Agenda , A g e n d a A t t r i b u t e s , . . . { 2 . . .

3 p r i v a t e i n t n b _ j o u r s _ l i b r e s = 0 ; 4 . . .

5 } 6

7 p u b l i c c l a s s G e s t i o n a i r e D e A b s e n c e imp lemen ts A bs ence , A b s e n c e A t t r i b u t e s , . . . { 8 . . .

9 p r i v a t e i n t n b _ j o u r s _ l i b r e s = 0 ; 10 . . .

11 }

La duplication des ressources va entraîner l’obligation de gérer la cohérence des copies. Pour cela, il faut mettre en place un système de gestion de cohérence et de synchronisation (voir Section 3.3).

Le code ainsi généré lors de cette étape de fragmentation représente la première version des codes sources des composants à générer. La prochaine étape consiste à transformer ce code de manière à prendre en compte les liens comportementaux existants entre les méthodes définies respectivement par deux SBDG différents. Cette transformation est réalisée pendant l’étape de recomposition.

Outline

Documents relatifs