Du découpage à l’assemblage non anticipé de composants

Quels sont les nouveaux concepts et mécanismes introduits par la programmation par composants ?

   Il est difficile aujourd’hui d’énoncer clairement les apports de la programmation par composants (PPC ou Component-Oriented Programming soit COP en anglais) comme on peut le faire pour la programmation par objets (PPO ou Object-Oriented Programming soit OOP en anglais). Dans le cas de la PPO, les deux langages fondateurs sont : Simula [Birtwistle et al., 1973], apparu en 1967 et Smalltalk [Goldberg et Robson, 1989], apparu en 1973. Les apports de ces langages par rapport aux langages procéduraux comme Pascal notamment sont les notions d’« objet » qui encapsule données et traitements, d’« envoi de message » qui permet à un objet receveur de décider de son comportement, d’« héritage » et de « spécialisation » qui permettent une meilleure réutilisation et une extension facilitée. Enoncer aussi clairement et précisément les apports de la PPC est actuellement difficile. On peut toutefois affirmer que le cœur de la PPC est certainement le mécanisme d’« assemblage » des composants. Toutefois, ce mécanisme n’est pas présent dans toutes les propositions, par exemple on ne le trouve pas dans le modèle EJB, et il peut se décliner sous des formes bien différentes dans les autres propositions. On peut aussi arguer qu’un des apports de l’approche à composants, par rapport à l’approche à objets notamment, est que les composants sont dotés d’« interfaces requises » qui permettent d’expliciter et de spécifier clairement les besoins d’un composant par rapport à son environnement. Un composant est également doté d’« interfaces fournies » le protégeant de tout accès direct mais cette notion est moins spécifique de l’approche composants car elle existait dans l’approche objets au travers des propriétés publiques des objets.

L’approche composants n’est-elle qu’une extension de l’approche à objets ?

  Actuellement, l’approche objet est considérée comme un paradigme de programmation uniforme dans le sens où il existe des langages de programmation comme Smalltalk où « tout est objet ». Dans ce contexte, peut-on imaginer la même chose pour l’approche composants  ou s’agit-il d’une approche ne pouvant se suffire à elle-même ? Apporter une réponse concrète à cette question est actuellement difficile car la majorité des langages à composants (LAC) sont des extensions de langages à objets où les objets et les composants cohabitent de façon non-uniforme. De plus, cela laisse supposer que tous les mécanismes objets font partie intégrante de l’approche à composants. Or, quelques approches à composants spécifiques comme Koala [Ommering, 1998] nous prouvent le contraire puisqu’elles sont implémentées avec des langages de programmation procéduraux comme le langage C. Finalement,répondre à cette question revient à identifier tous les mécanismes nécessaires et suffisants pour faire de la programmation par composants.

A-t-on besoin de langages de programmation dédiés à l’approche composants ?

   Oui ! Nous pensons en effet que des langages de programmation dédiés à l’approche composants sont nécessaires car :
– Les langages de description d’architectures [Medvidovic et Taylor, 2000] ne permettent généralement pas d’écrire des descriptions exécutables. Nous sommes en accord complet avec l’affirmation de Cointe et al. suivante [Cointe et al., 2004] : « L’approche langage (de programmation) nous semble devoir être privilégiée car elle autorise une définition concise des modèles de composants et de leurs systèmes d’assemblage mais également car elle permet de disposer de descriptions exécutables. »
– Bien que les langages de programmation existants (procéduraux ou à objets) soient largement utilisés pour faire de la PPC, ils imposent aux programmeurs de respecter des règles de transformations entre les concepts de l’approche à composants et ceux du langage cible. Des exemples simples de transformations sont : un composant peut être représenté par une classe, les interfaces des composants peuvent être représentées par des interfaces de classes et les interactions entre les composants peuvent définies par des associations ou des envois de messages. L’utilisation de transformation n’est pas satisfaisant pour faire de la PPC car cela complexifie la programmation à cause de l’utilisation de conventions de nommage ou de schémas de conception dans les cas complexes. De plus, les principaux apports de l’approche à composants sont alors perdus au niveau du code source [Perry et Wolf, 1992].
– Les approches à composants actuelles qui permettent de faire de la PPC comme ArchJava [Aldrich et al., 2002b], Julia (implémentation de référence de Fractal [Bruneton et al., 2004]) ou encore Lagoona [Fröhlich et al., 2005] sont disparates. De plus, la plupart de ces langages sont des extensions de langages à objets dans lesquels coexistent les objets et les composants de façon non-uniforme. Tout cela rend difficile la compréhension et la pratique de la PPC.
– Même si on adopte une approche d’ingénierie dirigée par les modèles (IDM ou Model Driven Engineering soit MDE en anglais) [Schmidt, 2006], l’utilisation de langages à composants unifiés reste nécessaire. En effet, la génération de code et la rétro-ingénierie seraient très certainement d’autant plus simples que le langage de programmation cible intègre des abstractions et des mécanismes de haut niveau. D’autre part, on ne peut pas envisager à l’heure actuelle de modifier le modèle et de générer à nouveau l’application pour chaque évolution, notamment les évolutions mineures comme les corrections de bugs. Il nous semble donc raisonnable de vouloir générer du code source suffisamment simple pour être maintenu et modifié directement dans certains cas ce qui suppose l’existence de langages à composants unifiés.

La réutilisation

   La réutilisation est un des objectifs majeurs du génie logiciel. De nombreux concepts et mécanismes associés ont été inventés pour réutiliser le code :
– fonction et appel de fonction,
– module et importation de module,
– classe et héritage,
– framework et paramétrage de framework,
– composant et assemblage de composants.
Dans tous les cas, la réutilisation s’effectue en deux étapes : abstraction et utilisation. L’abstraction consiste à définir sous une forme abstraite et indépendante de tout contexte ce qui peut être réutilisé. L’utilisation consiste à réutiliser dans un contexte donné une abstraction, ce qui peut nécessiter des adaptations et/ou du paramétrage. Par exemple, la définition d’une fonction s’effectue avec des paramètres formels (abstraction) qui sont substitués par des paramètres réels (paramétrage) lors d’un appel de la fonction. Un mécanisme de réutilisation de code définit donc comment décrire les abstractions et comment les utiliser ensuite. On distingue généralement trois types de réutilisation : boîte noire, boîte blanche et boîte grise. Avec l’approche boîte noire, l’implémentation de l’abstraction n’est pas connue lors de son utilisation et le paramétrage s’effectue à travers des interfaces définies explicitement. Typiquement, une fonction peut être considérée comme une boîte noire paramétrable uniquement via ses paramètres qui constituent son interface. Ce type de réutilisation présente l’avantage que l’abstraction peut être aisément changée par une autre présentant les mêmes interfaces. Par exemple, changer l’implémentation d’une fonction sans changer sa spécification externe (signature et sémantique) permet à tous les programmes utilisant cette fonction de profiter de la nouvelle définition sans qu’il soit nécessaire de changer leur code. On parle ici d’une fonction soit sans « effet de bord » soit ayant les mêmes « effets de bord ». La réutilisation de type boîte blanche repose sur le fait que les détails de l’implémentation de l’abstraction sont connus lors de son utilisation et le paramétrage peut s’effectuer soit à travers les interfaces, soit directement à travers son implémentation. Ce type de réutilisation présente l’avantage de fournir toutes les informations sur le fonctionnement interne de l’abstraction et des possibilités de paramétrage plus fines. Par exemple, cette forme de réutilisation est utilisée pour les frameworks objets. Les interfaces explicites de paramétrage d’un framework sont les méthodes abstraites déclarées dans les classes qui le constituent. Il est donc possible grâce à la spécialisation de classes de paramétrer un framework en définissant les méthodes abstraites ou en redéfinissant des méthodes existantes dans des sous-classes. La redéfinition de méthodes existantes dans le framework constitue une réutilisation de type boîte blanche. Cette forme de réutilisation est problématique du point de vue des changements. En effet, si une nouvelle implémentation du framework est produite, sans changement de son interface (méthodes et classes abstraites), les programmes clients qui paramétraient le framework en redéfinissant directement ses méthodes peuvent ne plus fonctionner. La réutilisation est donc confrontée à un paradoxe. Pour augmenter le potentiel de réutilisation d’une abstraction, indépendamment d’un contexte d’utilisation, il faut la considérer comme une boîte noire ayant des interfaces explicites. Or, pour une utilisation dans un contexte donné, des mécanismes permettant le paramétrage et l’adaptation de l’abstraction sont parfois nécessaires, ce qui constitue une approche de type boîte blanche. La réutilisation de type boîte grise est un niveau intermédiaire entre les deux formes précédentes. Les détails d’implémentation de l’abstraction peuvent être connus ou révélés pour comprendre sa réalisation mais ne peuvent pas être modifiés par ses clients et son utilisation ne peut s’effectuer qu’à travers ses interfaces. Actuellement, l’approche objets est la plus utilisée pour le développement de logiciel. Pourtant, la réutilisation de code basée sur l’héritage entre les classes est assez problématique comme le met en évidence, par exemple et entre autres, le problème de la classe de base fragile [Mikhajlov et Sekerinski,1998]. Ce problème met en évidence le fait que les modifications (du fait des évolutions par exemple) apparemment sûres des classes de base (super-classes) peuvent tout même causer de mauvais fonctionnements dans les sous-classes. Un programmeur ne peut donc déterminer si un changement est sûr en examinant uniquement la classe dans laquelle a eu lieu le changement. Nous reviendrons plus précisément sur ce problème dans la section 3.13 qui traite de l’héritage. Par ailleurs, une classe ne constitue pas une unité de réutilisation satisfaisante comme l’explique [Flatt, 2000] : « […] class-based languages encourage the reuse of class definitions through extension, but they do not permit the reuse of a class extension in disjoint parts of a class hierarchy. » Une classe n’est en effet pas indépendante de tout contexte puisqu’elle est liée à la hiérarchie dans laquelle elle a été définie. Ce problème du couplage implicite [Briand et al., 1999; Peschanski et al., 2000] se traduit par le fait que dans le code des méthodes, il est possible d’instancier ou d’utiliser des éléments externes sans que ces liens soient explicités via des interfaces permettant ainsi une réutilisation boîte noire et du paramétrage. La figure 2.1 montre un exemple de couplage implicite entre deux objets. Chaque objet instance de la classe A utilisera sa propre instance de la classe B pour remplir la fonctionnalité bar. Ce couplage entre les objets est implicite car noyé dans le code source qui n’est pas toujours visible et il est aussi fort car non modifiable.

La répartition et l’interopérabilité

  La multiplication du nombre des réseaux informatique a profondément bouleversé le développement des applications. Ces dernières ont été dotées de fonctions pour communiquer ou pour accéder à des ressources distantes. La nécessité de découplage entre la partie métier et la partie technique d’une application (comme les communications distantes, la sécurité, etc.) a conduit à l’élaboration d’intergiciels (aussi appelés bus logiciels, middleware en anglais). Un intergiciel (cf. figure 2.4) est une couche logicielle intermédiaire installée sur les systèmes d’exploitation ayant pour objectif d’offrir une vision uniforme et transparente aux applications communicantes en masquant la répartition géographique, l’hétérogénéité des systèmes, des matériels et des protocoles de communication.  L’utilisation d’un intergiciel permet d’assurer une indépendance entre le code métier des applications et le code technique qui doit faire face à de nombreuses problématiques de bas niveau comme la concurrence ou encore les pannes physiques. Un intergiciel offre généralement un ensemble de services communs de haut niveau (aussi appelés services non-fonctionnels) comme le contrôle de concurrence, les transactions ou la sécurité. Les intergiciels à objets ou ORB (Object Request Broker) permettent l’invocation distante d’une méthode d’un objet. Plusieurs intergiciels à objets existent dont le plus connu est certainement le bus logiciel normalisé CORBA (Common Object Request Broker Architecture) de l’OMG (Object Management Group). Toutefois, les architectures à objets répartis ont évolué vers des architectures à composants répartis. Par exemple, l’OMG propose actuellement le modèle de composants CCM (Corba Component Model) qui est une évolution du modèle d’objets CORBA. Cette évolution vers les composants dans le domaine des intergiciels a été notamment motivée par les problèmes de couplage évoqués dans la section 2.1.1, mais aussi par la difficulté à bien séparer les préoccupations métiers et techniques (aussi appelées non-fonctionnelles) en utilisant une approche à objets. Avec les composants, le code d’accès aux services non-fonctionnels n’est pas mélangé avec le code métier. Pour cela, les communications entre la couche métier et la couche technique sont contrôlées par une tierce entité au sein de la plateforme intergicielle. C’est donc cette tierce entité qui utilise le code métier lorsque cela est nécessaire et non l’inverse. Ce principe d’externalisation est parfois appelé inversion de contrôle (control inversion) et il est similaire au fonctionnement des frameworks où le code écrit par un programmeur est appelé par le code du framework. Dans le modèle EJB détaillé dans la section 2.2.4, cette tierce entité est appelé un conteneur et supporte l’exécution d’un composant EJB.

Les principales familles d’approches à composants

  Bien qu’il n’existe pas de classification générale des approches à composants, la littérature [Marvie, 2002; Oussalah, 2005] distingue souvent :
– les approches industrielles comme (D)COM(+)/.NET ou EJB,
– les approches académiques comme les langages de description d’architectures (ADLs) (Unicon, C2, WRIGHT, ACME, etc.) ou encore d’autres propositions telles que ArchJava et ComponentJ,
– les normes (ou modèles de référence) comme CCM ou UML 2.0.
Cette classification permet d’expliquer de façon générale les différents objectifs visés par ces différentes propositions. Toutefois, elle ne permet pas de classer facilement toutes les approches à composants comme par exemple l’approche Fractal qui peut être classée dans les trois catégories puisqu’elle est réalisée dans le cadre d’un groupement entre industriels et chercheurs, que le modèle Fractal est une norme et que son implémentation de référence Julia est un langage. Les approches industrielles visent à fournir un cadre concret permettant le développement d’applications réparties par composants. Elles reposent bien souvent sur des solutions techniques qui rendent complexe l’apprentissage de ce mode de développement. Les approches académiques sont assez disparates. On distingue généralement les ADLs [Medvidovic et Taylor, 2000], qui permettent de décrire des architectures logicielles, des autres propositions. Les ADL sont des langages spécialisés n’ayant pas tous les mêmes objectifs mais ils utilisent bien souvent des concepts similaires : composant, connecteur et configuration. On peut distinguer au moins trois sortes d’ADLs [Marvie, 2002] :
– formels, comme WRIGHT, dont la motivation est de capturer le comportement des applications à des fins de vérifications automatisables,
– de communication, comme ACME, qui permettent l’échange ou l’intégration d’éléments architecturaux définis à l’aide de langages différents,
– de configuration, comme C2, dont l’objectif est d’exploiter les descriptions d’architectures dans le but de générer en partie les composants logiciels ou de supporter l’automatisation du processus de déploiement des applications. Les ADLs sont une réponse au besoin d’exprimer clairement la structure des applications afin de passer à un mode de développement à grande échelle (programming in the large) contrairement aux langages à objets qui à la base sont plus spécialisés dans le développement à petite échelle (programming in the small). Toutefois, les descriptions architecturales sont souvent peu reliées à l’implémentation d’une application qui s’effectue toujours dans des langages de programmation tels les langages à objets. Bien que des squelettes de code puissent être générés, il est impossible de garantir que les propriétés architecturales soient effectivement respectées par l’implémentation. Cette constatation est à l’origine du langage ArchJava [Aldrich et al., 2002b] (décrit dans la section 2.2.8). Ce langage hybride tente de faire le lien entre un langage de programmation (Java) et les concepts des ADLs. D’autres approches hybrides similaires existent comme Lagoona [Fröhlich et al., 2005], ComponentJ [Seco et Caires, 2000], ou encore keris [Zenger, 2005]. Les normes ou modèles de référence comme CCM ou UML 2.0 permettent de concilier les approches « haut niveau » des modèles académiques avec les préoccupations pratiques des approches industrielles. Par exemple, UML 2.0 propose un langage graphique de modélisation permettant de décrire un système en terme de composants interconnectés. Ce langage normalisé permet l’échange de modèles architecturaux au même titre que certains ADLs. Le modèle CCM, décrit dans la section 2.2.5, est une extension du modèle des objets distribués CORBA pour supporter le développement par composants distribués. Face à cette diversité, nous avons choisi de présenter : (D)COM(+) pour des raisons historiques puisqu’il s’agit de l’un des premiers exemples qualifiés d’approche à composants, Javabeans car ce modèle — souvent confiné à tort dans le domaine de la construction d’interfaces graphiques — est simple et a apporté selon nous un modèle d’assemblage puissant et novateur encore utilisé aujourd’hui, EJB car cette approche est particulièrement utilisée actuellement dans les milieux industriels, CCM car il s’agit d’une norme incontournable, Fractal car il s’agit d’un modèle récent et unificateur actuellement très utilisé dans le monde académique, SOFA car bien qu’il soit moins utilisé que Fractal, ce modèle académique propose une vision différente en se focalisant sur les connecteurs ou le remplacement dynamique de composants, ArchJava car il s’agit d’une approche particulière axée sur le langage de programmation offert aux programmeurs pour implémenter effectivement les composants, contrairement aux autres approches qui utilisent les langages de programmation existants, UML puisqu’il constitue un standard de fait incontournable surtout depuis sa version 2.0 intégrant les structures composites. Nous avons délibérément écarté l’étude d’un ADL : d’une part, car il est difficile d’en choisir un qui soit représentatif – ils ont tous leurs spécificités ; d’autres part, nous nous intéressons dans cette thèse plus à la programmation qu’à la description d’architectures. Toutefois, nous présentons les approches Fractal, SOFA ou encore ArchJava qui sont des approches hybrides intégrant des concepts issus des ADLs. Finalement, une comparaison détaillée des principaux ADL est disponible dans la littérature [Medvidovic et Taylor, 2000].

Javabeans

Le modèle Le modèle de composants Javabean a été développé par Sun Microsystems en 1996 autour de son langage Java. Un Javabean est un « composant logiciel réutilisable qui peut être manipulé graphiquement dans un environnement de développement » [Hamilton, 1997]. Il faut bien comprendre que tous les Javabeans ne sont pas nécessairement des composants graphiques (appelés widgets) comme des boutons, des barres de menus, etc. Même si ce modèle est particulièrement bien adapté pour la construction d’interfaces graphiques, son utilisation peut être beaucoup plus large. Un Javabean est une instance d’une classe Java, qui possède des attributs, des méthodes, des propriétés et peut émettre et recevoir des événements (cf. figure 2.9). Les attributs et les méthodes sont des concepts standards en Java, contrairement aux propriétés qui sont des « unités » sémantiques publiques qui affectent l’apparence ou le comportement d’un Javabean. Une propriété possède un nom, un type et une valeur qui est accessible en lecture et/ou écriture via des méthodes du Javabean. On distingue les propriétés qui n’ont qu’une seule valeur de celles qui ont plusieurs valeurs (indexed properties). Les événements sont des objets Java que les composants s’échangent lorsqu’ils sont connectés. Il existe une multitude d’événements prédéfinis et il est possible d’en définir de nouveaux. Nous verrons dans la suite des exemples d’événements, notamment relatifs aux changements de valeurs des propriétés.

Le rapport de stage ou le pfe est un document d’analyse, de synthèse et d’évaluation de votre apprentissage, c’est pour cela chatpfe.com propose le téléchargement des modèles complet de projet de fin d’étude, rapport de stage, mémoire, pfe, thèse, pour connaître la méthodologie à avoir et savoir comment construire les parties d’un projet de fin d’étude.

Table des matières

Liste des figures
Liste des tableaux
Liste des listings
Remerciements
1 Introduction 
1.1 Présentation de l’approche à composants
1.1.1 Eléments de base de l’approche à composants
1.1.2 Objectifs de l’approche à composants
1.1.3 Un processus de développement centré sur la réutilisation
1.2 Motivations et objectifs de la thèse 
1.3 Organisation de ce mémoire
2 Synthèse comparative des approches à composants 
2.1 Pourquoi des composants logiciels ? 
2.1.1 La réutilisation
2.1.2 Le passage à l’échelle
2.1.3 La répartition et l’interopérabilité
2.2 Présentation détaillée des principales approches à composants
2.2.1 Les principales familles d’approches à composants
2.2.2 (D)COM(+)
2.2.3 Javabeans
2.2.4 Enterprise JavaBeans (EJB)
2.2.5 Corba Component Model (CCM)
2.2.6 Fractal
2.2.7 SOFA/DCUP
2.2.8 ArchJava
2.2.9 UML 2.0
2.3 Comparaisons des approches à composants 
2.3.1 Les objectifs visés
2.3.2 Niveaux d’abstraction
2.3.3 Comparaison générale
2.3.4 Niveaux de découplage
2.3.5 Assemblage
2.3.6 Synthèse
2.4 Conclusion
3 Spécification de SCL : un langage à composants minimal 
3.1 Motivations et Objectifs 
3.2 Vocabulaire de base 
3.3 Faut-il des descripteurs de composant ?
3.4 Comment représenter les fonctionnalités des composants ? 
3.5 Que sont les prises des composants ? 
3.5.1 Notion de port
3.5.2 Notion d’interface
3.5.3 Exemple en SCL
3.6 Les objets primitifs sont-ils des composants ?
3.7 Qu’est-ce que lier des prises de composants ? 
3.8 Les liaisons multiples 
3.9 Les bases de l’invocation de services
3.9.1 Le port d’émission
3.9.2 Les paramètres
3.10 Les connexions doivent-elles être réifiées ? 
3.11 De l’intérêt des composants composites ? 
3.12 L’invocation de services 
3.13 Faut-il de l’héritage ? 
3.14 Synthèse
3.15 Bilan 
4 Séparation des préoccupations en SCL 
4.1 Présentation de la programmation par aspects 
4.1.1 Point de jonction
4.1.2 Point de coupe
4.1.3 Advice
4.1.4 Déclaration « inter-type »
4.1.5 Aspect
4.1.6 Tissage
4.1.7 Synthèse et remarques
4.2 Aspects et approches à composants 
4.2.1 Aspects et approches à composants industrielles
4.2.2 Aspects et modèles de composants
4.2.3 Aspects et langages à composants
4.3 Séparer les préoccupations en SCL 
4.3.1 Quels points de jonction ?
4.3.2 Qu’est-ce qu’un advice ?
4.3.3 Comment effectuer le tissage ?
4.3.4 Critique
4.4 Les propriétés observables des composants 
4.4.1 Problématique
4.4.2 Les solutions existantes
4.4.3 Notre solution en SCL basée sur les aspects
4.5 Conclusion
5 Prototypes de SCL 
5.1 Pourquoi Smalltalk ? 
5.2 Choix d’implémentation 
5.3 Amorçage de l’implémentation 
5.4 Architecture générale du prototype 
5.5 Le modèle implémenté
5.6 L’intégration des objets de base 
5.7 L’invocation de service 
5.8 Les liaisons et les connecteurs 
5.9 Les propriétés 
5.10 Vers un environnement de développement graphique 
5.11 Le noyau d’un prototype en Ruby 
5.12 Conclusion 
6 Bilan et Perspectives 
A Programmation par composants avec des langages de programmation actuels
A.1 PPC en C
A.2 PPC en Java
B Environnements de programmation visuels basés sur l’approche à composants
B.1 Quartz Composer
B.2 Automator
B.3 Pipes
B.4 Scratch
Bibliographie
Publications

Rapport PFE, mémoire et thèse PDFTélécharger le rapport complet

Télécharger aussi :

Laisser un commentaire

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