These Luc Fabresse
These Luc Fabresse
UNIVERSITÉ MONTPELLIER II
— S CIENCES ET T ECHNIQUES DU L ANGUEDOC —
T HÈSE
S PÉCIALITÉ : I NFORMATIQUE
Formation Doctorale : Informatique
École Doctorale : Information, Structures, Systèmes
par
Luc FABRESSE
Philippe C OLLET, Maître de conférences, I3S, Université de Nice Sophia Antipolis, . . . . . . . . . . . . . Co-Rapporteur
Christophe D ONY, Professeur, LIRMM, Université Montpellier II, . . . . . . . . . . . . . . . . . . . . . . . . . . . Directeur de Thèse
Jacques F ERBER, Professeur, LIRMM, Université Montpellier II, . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Examinateur
Marianne H UCHARD, Professeur, LIRMM, Université Montpellier II, . . . . . . . . . . . . . . . . . . . . . . . . Directrice de Thèse
Philippe L AHIRE, Professeur, I3S, Université de Nice Sophia Antipolis, . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Rapporteur
Mourad O USSALAH, Professeur, LINA, Université de Nantes, . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .Rapporteur
Guy T REMBLAY, Professeur, UQÀM, Université du Québec à Montréal, . . . . . . . . . . . . . . . . . . . . . . . . . . . . Examinateur
Table des matières
Remerciements xiii
1 Introduction 1
1.1 Présentation de l’approche à composants . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
1.1.1 Eléments de base de l’approche à composants . . . . . . . . . . . . . . . . . . . 2
1.1.2 Objectifs de l’approche à composants . . . . . . . . . . . . . . . . . . . . . . . . 4
1.1.3 Un processus de développement centré sur la réutilisation . . . . . . . . . . . . 5
1.2 Motivations et objectifs de la thèse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
1.3 Organisation de ce mémoire . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
iii
iv Table des matières
5 Prototypes de S CL 141
5.1 Pourquoi Smalltalk ? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 142
5.2 Choix d’implémentation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 142
5.3 Amorçage de l’implémentation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 143
5.4 Architecture générale du prototype . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 144
5.5 Le modèle implémenté . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 145
5.6 L’intégration des objets de base . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 147
5.7 L’invocation de service . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 149
5.8 Les liaisons et les connecteurs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 151
5.9 Les propriétés . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 152
5.10 Vers un environnement de développement graphique . . . . . . . . . . . . . . . . . . . 154
5.11 Le cœur d’un prototype en Ruby . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 155
5.12 Conclusion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 157
Bibliographie 171
Publications 183
Liste des figures
1.1 Vision simplifiée du processus de développement par composants à travers les deux rôles
centraux du développeur et de l’architecte. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
3.1 Les processus Unix (pipes and filters) vus comme des composants . . . . . . . . . . . . . . 78
3.2 Présentation de conventions graphiques adaptées à S CL . . . . . . . . . . . . . . . . . . . . 81
vii
viii Liste des figures
ix
Liste des listings
xi
xii Liste des listings
xiii
1
CHAPITRE
Introduction
Préambule
1
2 Chap 1. Introduction
I
L existe actuellement en ingénierie logicielle un intérêt grandissant pour les techniques et les outils
permettant de développer des applications par assemblage de composants logiciels. Cet intérêt
pour les composants résulte aussi bien de la volonté de réduire les coûts de développement en aug-
mentant la réutilisation que de la nécessité d’inventer de nouvelles formes de développement pour
prendre en compte la complexité structurelle sans cesse croissante des applications liée à de nou-
veaux besoins comme la répartition, la fiabilité ou l’évolution. Cette nouvelle ère de l’orienté compo-
sants commence à peine à se développer alors que l’idée fut proposée pour la première fois en 1968
par McIlroy [McIlroy, 1968]. Originellement, cette approche se fonde sur une analogie entre un élec-
tronicien qui fabrique un circuit par assemblage de composants électroniques via des connexions
supportant le passage des électrons et un informaticien qui pourrait construire une application in-
formatique par assemblage de composants logiciels réutilisables via des « connexions » supportant
les communications et les échanges de données entre les composants. Actuellement, cette approche
nécessite encore d’être précisée, outillée et expérimentée afin de réellement prendre son essor. Cette
idée, encore d’actualité, est parfaitement exprimée dans [Brown et Wallnau, 1998] :
Avant de poser la problématique de cette thèse (cf. section1.2), la suite de cette section est consa-
crée à la présentation générale du développement d’applications par assemblage de composants à
travers ses éléments de base, ses objectifs et son processus de développement centré sur la réutilisa-
tion.
Deux notions sont centrales et complémentaires dans l’approche composants : les composants
logiciels et les architectures logicielles.
Un composant logiciel est une brique logicielle élémentaire permettant la construction d’appli-
cations. Donner une définition plus précise ou plus formelle de ce terme dans sa généralité reste
encore une question ouverte à ce jour. En effet, il existe actuellement autant de propositions de dé-
finition [Broy et al., 1998] que d’utilisations différentes des composants. Il est même avancé [Gröne
et al., 2005] que le terme «composant» ne peut posséder une unique définition puisque suivant le
contexte, ce terme désigne aussi bien des schémas de conception (design patterns) [Gamma et al.,
1995], des fonctions ou procédures [McIlroy, 1968], des modules [Fröhlich et al., 2005], des cadres
d’application (frameworks) [Szyperski, 2002], des classes [Hamilton, 1997], ou encore des applica-
tions complètes [Microsoft, 1996]. On peut tout de même s’appuyer sur les définitions les plus répan-
dues dans la littérature afin de mieux cerner ce concept. La définition la plus consensuelle, proposée
en 1996 lors d’un atelier de travail sur la programmation orientée composants [WCO, 1996], est :
Cette définition, relativement générale, peut s’interpréter à différents niveaux d’abstraction (spé-
cification, conception, implémentation). Elle donne les caractéristiques externes d’un composant
(interfaces, dépendances explicites) et met l’accent sur la capacité des composants à être assemblés1
afin de construire des applications. La vision, plus technique, des composants proposée par le SEI
(Software Engineering Institute) [Bachman et al., 2000] est qu’ils sont des unités de réutilisation, de
distribution et de déploiement possédant des limites et des dépendances claires et explicites, pou-
vant être paramétrées (customized) et assemblées. Pour finir, une vision plus précise est proposée
dans [Heineman et Councill, 2001] dont voici quelques extraits :
« The architecture of a software system defines that system in terms of components and
of interactions among those components. In addition to specifying the structure and topo-
logy of the system, the architecture shows the intended correspondence between the system
requirements and elements of the constructed system. It can additionally address system-
level properties such as capacity, throughput, consistency, and component compatibility.
Architectural models clarify structural and semantic differences among components and
interactions. Architectural definitions can be composed to define larger systems. Elements
are defined independently so they can be re-used in different contexts. »
Une architecture logicielle définit la structure macroscopique d’un logiciel en termes de com-
posants, qui assurent les fonctions de calculs, et de connecteurs [Shaw, 1996; Mehta et al., 2000] qui
relient les composants et coordonnent leurs interactions pour satisfaire des contraintes globales d’in-
tégrité (invariants structurels, coordination, etc.) et des contraintes de qualité (fiabilité, sécurité, évo-
lutivité, etc.).
1 En français, on préfère le terme « assemblage » à celui de « composition » car la composition de composants n’est
A l’instar de tout projet, le développement d’un logiciel nécessite de maîtriser au mieux les coûts
et le temps. Ces deux ressources doivent être maîtrisées durant tout le cycle de vie d’un logiciel ce qui
inclut son développement, ses phases de tests mais aussi sa maintenance et ses inévitables évolu-
tions comme le signale la première loi de Lehman [Lehman et Belady, 1985]. Cette gestion est de plus
en plus difficile notamment à cause d’une part de la taille des logiciels, et d’autre part du dévelop-
pement des réseaux de communications. En effet, la taille des logiciels ne cesse de croître (on peut
citer comme exemple la suite bureautique OpenOffice.org2 qui a dépassé les 10 millions de SLOC,
Source Line of Code). D’autre part, l’essor des réseaux informatiques a doté les logiciels de fonctions
communicantes qui incluent la distribution mais aussi les possibilités d’interactions avec d’autres
applications ou systèmes qui évoluent indépendamment. Dans ce nouveau contexte de développe-
ment d’applications à grande échelle, le développement par composants promet, en ce qui concerne
le coût et le temps de développement, les améliorations suivantes :
– La diminution des coûts et du temps de développement et de test par la réutilisation ou l’achat
de composants existants ;
– La réduction du temps de test et de mise au point des logiciels grâce à l’utilisation de compo-
sants testés et éprouvés ;
– La réduction des coûts de maintenance et d’évolution des logiciels car ils sont constitués de
composants découplés pouvant être remplacés indépendamment les uns des autres. Cette pro-
priété est appelée l’« extensibilité indépendante » [Szyperski, 2002] (en anglais independent ex-
tensibility) des applications à base de composants ;
– Une simplicité accrue pour le développement d’applications puisqu’il n’est pas nécessaire de
comprendre le fonctionnement interne des composants. Il suffit en effet de connaître les fonc-
tionnalités qu’ils peuvent accomplir ainsi que la façon dont ils communiquent avec leur envi-
ronnement.
L’approche composants promet aussi une meilleure qualité logicielle. Considérons le modèle de
qualité défini par la norme 9126-1 de l’ISO/IEC [Standard, 2001] (International Organization for Stan-
dardization/International Electrotechnical Commission) qui est l’un des modèles les plus utilisés. Ce
modèle définit six facteurs primaires de qualité : la capacité fonctionnelle (le logiciel répond-il aux be-
soins fonctionnels, de sécurité et d’interopérabilité ?), la fiabilité (le logiciel est-il capable de fournir
ses services dans des conditions et sur une durée spécifiées dans le cahier des charges ?), l’efficacité
(le logiciel est-il rapide et économe en consommation mémoire ?), l’utilisabilité (le logiciel est-il facile
à utiliser ?), la maintenance (le logiciel est-il facilement modifiable, testable, adaptable ?) et la porta-
bilité (le logiciel peut-il être facilement installé dans un autre environnement ?). Adopter l’approche
à composants, par rapport à l’approche à objets notamment, promet un gain de qualité sur au moins
trois de ces six facteurs : maintenance, fiabilité et portabilité. Toutefois, il est difficile d’évaluer à ce
jour l’impact réel de l’utilisation de l’approche à composants et notamment si les trois autres facteurs
ne sont pas dégradés.
2 http://www.openoffice.org
1.1. Présentation de l’approche à composants 5
De plus en plus de travaux visent à définir des méthodes de développement adaptées à l’approche
à composants [D’Souza et Wills, 1999; Hassine et al., 2003; Mei, 2004; Atkinson et al., 2001]. La défini-
tion de ces nouvelles méthodes de développement a fait émerger de nouveaux acteurs tels que l’archi-
tecte, le développeur ou encore le déployeur [Marvie, 2002]. Deux acteurs nous intéressent plus parti-
culièrement : le développeur de composants et l’architecte d’application. Les rôles complémentaires de
ces deux acteurs (cf. figure 1.1) sont centraux dans le processus de développement par composants.
Le développeur a pour responsabilité de définir et implémenter des composants indépendamment
de toute application de sorte qu’ils puissent être utilisés dans différents contextes. À l’inverse, l’archi-
tecte construit une application. Il choisit donc des composants sur étagères, les adapte si nécessaire
pour les intégrer à son application. On dit que le développeur fait de la conception pour la réutilisa-
tion (« design for reuse ») alors que l’architecte fait de la conception par la réutilisation (« design by
reuse ») [Oussalah, 2005]. Il est évident qu’une même personne peut exercer ces deux rôles et qu’un
composant peut être créé en vue d’être intégré dans une application donnée. Ce nouveau composant
développé doit être complètement indépendant de l’application pour laquelle il est conçu afin qu’il
puisse éventuellement être réutilisé dans une autre application.
Développeur Architecte
Application
A C D
Composant
est est
B D D A
stocké utilisé
F IG . 1.1 : Vision simplifiée du processus de développement par composants à travers les deux rôles
centraux du développeur et de l’architecte.
Dans cette thèse, nous nous concentrons sur ces deux rôles centraux du processus de développe-
ment incarnés par le programmeur et l’architecte qui sont à la fois complémentaires et obligatoire-
ment distincts.
6 Chap 1. Introduction
Quels sont les nouveaux concepts et mécanismes introduits par la programmation par compo-
sants ?
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 pro-
grammation 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 Small-
talk [Goldberg et Robson, 1989], apparu en 1973. Les apports de ces langages par rapport aux lan-
1.2. Motivations et objectifs de la thèse 7
gages 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 fa-
cilité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 rap-
port à 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 en-
vironnement. 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’ap-
proche objets au travers des propriétés publiques des objets.
En résumé, dans cette thèse nous défendons que les langages de programmation à compo-
sants sont nécessaires, disparates dans l’existant et ne doivent pas être des extensions de
langages à objets car ils proposent des mécanismes inutiles voire nuisibles pour faire de la
PPC. Notre problématique est donc la suivante :
Identifier les concepts et les mécanismes fondamentaux de l’approche à compo-
sants, les expliciter clairement au sein d’un modèle de composants et proposer un
langage à composants minimal qui permette de programmer simplement des ap-
plications par assemblage de composants.
En réponse à cette problématique, nous proposons dans cette thèse le langage à composants S CL
(Simple Component Language). Les objectifs qui ont guidé la spécification de S CL sont parfaitement
énoncés dans [Wuyts et Ducasse, 2001]3 :
« [...] we feel that there is need for pure component languages. These languages are needed
to provide a component developer with a clean and concise vocabulary and semantics for
building and composing components. »
Plus spécifiquement, voici quelques questions à propos des langages à composants (LAC) — dont
la plupart ne sont pas abordées dans les travaux existants — auxquelles nous répondons dans cette
thèse à travers la conception de S CL :
– Qu’est-ce qu’un langage à composants et que doit-il permettre ?
3 Pour information, nous ne connaissions pas cet article lors de la conception de S CL , ce qui semble indiquer une
Outre ce chapitre d’introduction, ce mémoire de thèse est organisé en cinq autres chapitres.
Le chapitre 2 dresse un état de l’art de l’approche à composants. Les motivations générales de
cette approche sont présentées à travers trois axes problématiques du génie logiciel : la réutilisation,
le passage à l’échelle et la répartition pour lesquels le développement par composants semble mieux
adapté que l’approche à objets notamment. Après avoir présenté les principales familles d’approches
à composants, certaines approches sont décrites de façon détaillée pour être ensuite comparées. Fi-
nalement, en conclusion de ce chapitre, nous montrons les faiblesses de ces approches en ce qui
concerne la programmation par assemblage de composants.
Le chapitre 3 présente la spécification du langage à composants S CL. Cette spécification est gui-
dée par des objectifs généraux qui sont définis en début de chapitre. Ensuite, chaque section présente
un besoin de la PPC, compare les solutions existantes et finalement intègre une solution en S CL, par-
fois nouvelle, tout en respectant les objectifs de départ. Avant de conclure ce chapitre, une synthèse
du noyau de S CL est présentée.
Le chapitre 4 présente une étude autour de la séparation des préoccupations dans les approches
à composants. Après avoir rappelé le principe de séparation des préoccupations, nous présentons
la programmation par aspects qui permet une meilleure modularisation de certaines préoccupations
dites « transversales » ainsi qu’une étude des principales approches mixtes à composants et à aspects.
Nous proposons ensuite deux extensions de S CL respectant les objectifs présentés dans le chapitre
précédent. La première permet d’utiliser un même composant S CL de façon standard ou de façon
transversale. Contrairement aux approches mixtes dites « symétriques » dont s’inspire cette exten-
sion, le programmeur d’un composant S CL n’a pas à écrire de code spécifique pour cela. De même,
la deuxième extension permet d’établir des connexions entre les composants basées sur les change-
ments d’états de leurs propriétés.
Le chapitre 5 présente les deux prototypes de S CL. Concernant le prototype le plus avancé, écrit en
Smalltalk, nous présentons nos choix de conception, le modèle implémenté, des exemples de code
ainsi qu’un prototype d’outil de développement graphique. Nous présentons ensuite le cœur d’un
10 Chap 1. Introduction
CHAPITRE
Synthèse comparative
des approches à composants
Préambule
Dans ce chapitre, nous présentons un état de l’art des travaux les plus représentatifs qui se réclament
du développement par assemblage de composants. La section 2.1 commence par une introduction rap-
pelant les principaux avantages — ou promesses — de l’approche à composants. Nous détaillons parti-
culièrement trois axes : la réutilisation, le passage à l’échelle et la répartition. Devant la multitude des
approches à composants, nous avons fait une sélection de celles que nous considérons comme princi-
pales afin de les détailler dans la section 2.2. La section 2.3 présente une comparaison des approches,
notamment celles étudiées en détails, suivant un ensemble de critères que nous jugeons pertinents pour
notre problématique. La section 2.4 conclut ce chapitre en proposant une vision synthétique des diffé-
rentes approches et montre la nécessité de proposer un nouveau langage à composants.
11
12 Chap 2. Synthèse comparative des approches à composants
S
UITE aux nouvelles contraintes auxquelles doit faire face le génie logiciel, les facteurs « coût » et
« temps » sont de plus en plus difficiles à maîtriser dans le cycle de vie d’un logiciel. Parmi ces
nouvelles contraintes, deux sont centrales : la taille croissante des applications et leur répartition qui
est due à l’essor des réseaux informatiques.
Cette section présente les trois principaux axes dans lesquels le développement par composants
promet d’être mieux adapté que le développement classique comme le développement par objets
notamment. Ces trois axes sont : (i) la réutilisation qui permet de réduire directement les coûts et le
temps lors des phases de développement et de test, (ii) le passage à l’échelle et (iii) la répartition qui
complexifie le code des applications en imposant l’intégration de code spécifique pour les communi-
cations. Chacun de ces trois axes est développé dans une sous-section avec l’objectif de montrer les
apports ou les promesses de l’approche à composants suivant cet axe.
2.1.1 La réutilisation
La réutilisation est un des objectifs majeurs du génie logiciel. De nombreux concepts et méca-
nismes 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 pa-
ramè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 abs-
tractions 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 utili-
sation 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émen-
tation d’une fonction1 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.
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 in-
terfaces, 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écla-
ré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 consti-
tue 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éca-
nismes permettant le paramétrage et l’adaptation de l’abstraction sont 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 fonc-
tionnements 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.
14 Chap 2. Synthèse comparative des approches à composants
A B
-b:B + void bar() {
+ A () { b=new B(); } ...
+ void foo() { }
b.bar();
}
A <<interface>>
- b : IB IB
app : Application void bar();
+ A () { }
+ void main (...) {
+ IB getB() { ... }
A a = new A();
+ void setB(IB bn) { ... }
IB b = new Bimpl();
+ void foo() {
a.setB(b);
... Bimpl
}
b.bar(); + void bar() {
... ...
} }
Une avancée vers plus de découplage serait que le constructeur de la classe A possède un para-
mètre de type B afin de valuer l’attribut privé b. Lors de l’instanciation de la classe A, il serait alors
possible de passer en argument une instance de la classe B. Le couplage serait alors effectué lors de
l’instanciation de la classe A et non pas statiquement dans son code. Ce découplage n’est toujours
pas satisfaisant car il n’autorise le paramétrage que lors de l’instanciation et non durant toute la vie
des objets de type A. L’utilisation d’accesseurs (get et set), d’une interface (au sens objet) et du sous-
typage (cf. figure 2.2) permet d’expliciter les liens de couplage entre objets et d’autoriser la modifica-
tion de ces liens durant l’exécution. La classe A utilise dans cet exemple l’interface IB pour typer son
attribut et définir ses accesseurs. Les signatures déclarées dans l’interface IB indiquent quelles sont
les fonctionnalités réalisées par un objet, ce qui n’introduit pas de couplage dans la classe A avec une
classe particulière comme la classe Bimpl qui indique comment sont réalisées des fonctionnalités
lorsqu’elle définit des méthodes.
Bien qu’il soit possible de réaliser le découplage avec un langage à objets, cela nécessite l’utilisa-
tion de conventions de programmation qui ne sont pas toujours respectées par les programmeurs. La
figure 2.3 montre un exemple schématisé et de haut niveau illustrant le fait que l’approche à compo-
sants impose le découplage entre les composants ainsi que l’explicitation des interfaces de communi-
cation. Dans cet exemple, le composant a appelle (call) un service bar qu’il ne définit pas (extern).
Cet appel sera traité par un autre composant, ici b, connecté au composant a lors de son assemblage.
En résumé, l’approche à composants prône l’utilisation systématique d’interfaces explicites afin
d’éviter les couplages faibles et implicites et une réutilisation de type boîte noire ou boîte grise. Cette
volonté de « protéger » les composants de leur contexte de définition et d’utilisation en explicitant
leurs dépendances via des interfaces a pour but de favoriser leur réutilisation. C’est ainsi que l’on
espère développer une réutilisation massive du code et même un marché de composants (component
2.1. Pourquoi des composants logiciels ? 15
<<interface>>
B
void bar();
a : Component b : Component
La taille du code source des applications étant en constante croissance, il est de plus en plus
difficile de comprendre, maintenir et tester les programmes. Pour appréhender la taille sans cesse
croissante des logiciels, on développe aujourd’hui des techniques de visualisation du code [Langelier
et al., 2005; Ducasse et al., 2006; Wettel et Lanza, 2007] afin de mieux percevoir l’architecture d’une
application mais aussi ses évolutions. Toutefois, reconstruire l’architecture d’une application [Pollet
et al., 2007] est une tâche complexe notamment car elle n’est pas représentée dans le code source
lorsqu’on utilise un langage à objets. En effet, les langages à objets sont de plus en plus décrits comme
permettant de faire de la programmation à petite échelle (programming in the small) [DeRemer et
Kron, 1975].
Actuellement, la programmation à grande échelle (programming in the large) consiste à partition-
ner un logiciel en modules de taille suffisamment importante pour posséder des interfaces clairement
spécifiées et peu sujettes aux modifications. Les interactions entre les différents modules d’une appli-
cation peuvent ensuite être définis indépendamment de leurs implantations et de leurs évolutions.
L’approche à composants et plus particulièrement sa capacité à mieux exprimer les architectures logi-
cielles [Shaw et al., 1995] en terme de composants interconnectés, semble proposer une vision adap-
tée pour la programmation à grande échelle où un module est un composant. Il existe d’ailleurs des
langages de haut niveau dédiés à la description, parfois à la programmation, à grande échelle comme
les ADLs (Architecture Description Languages) qui s’attachent à décrire la structure ou le comporte-
ment des architectures logicielles en terme de composants et de connecteurs. La description des ar-
chitectures logicielles est donc le point de départ permettant d’appréhender une architecture dans sa
globalité et ainsi proposer des techniques de haut niveau adaptées aux architectures à grande échelle
comme le modèle d’évolution d’architecture SAEV [Oussalah et al., 2006] ou encore un mécanisme
de remplacement automatique d’un composant par un ensemble de composants interconnectés tout
en préservant la qualité de l’architecture de départ [Desnos et al., 2007].
16 Chap 2. Synthèse comparative des approches à composants
Application Application
A B
Adapteur Adapteur
pour A pour B
Adaptateur
pour C
Application
C
L’utilisation d’un intergiciel permet d’assurer une indépendance entre le code métier des applica-
tions 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 Mana-
gement 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 commu-
nications entre la couche métier et la couche technique sont contrôlées par une tierce entité au sein
2.2. Présentation détaillée des principales approches à composants 17
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 (in-
version of control) et il est tout à fait 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 sec-
tion 2.2.4, cette tierce entité est appelé un conteneur et il supporte l’exécution d’un composant EJB.
Dans cette section, nous allons décrire de façon détaillée un ensemble de propositions à base de
composants. Comme le montre le tableau 1.1 dans le chapitre 1, il existe de plus en plus d’approches
s’inscrivant dans le contexte du développement par composants. La section 2.2.1 présente les princi-
pales familles d’approches ainsi que les raisons qui nous ont conduits à présenter de façon détaillée
les approches (D)COM(+), Javabeans, EJB, CCM Fractal, SOFA, ArchJava et UML 2.0. Chacune des
sections suivantes est consacrée à l’étude d’une des approches précédentes et est structurée selon le
plan suivant :
1. Étude du modèle
a) Historique et présentation générale
b) Structure des composants
i. Structure externe
ii. Structure interne
c) Assemblage des composants
i. Types de liens de connexion
ii. Types de communications possibles
iii. Vérification des assemblages
d) Spécificités du modèle (héritage, déploiement, dynamicité, etc.)
2. Exemple
3. Critique
Bien qu’il n’existe pas de classification générale des approches à composants, la littérature [Mar-
vie, 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, W RIGHT, 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.
18 Chap 2. Synthèse comparative des approches à composants
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 à compo-
sants 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’ap-
plications 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 [Medvi-
dovic 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 sou-
vent des concepts similaires : composant, connecteur et configuration. Comme dans [Marvie, 2002],
on peut distinguer au moins trois sortes d’ADLs :
– 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 archi-
tecturaux 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 pro-
cessus 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 (program-
ming in the small). Toutefois, les descriptions architecturales sont souvent peu reliées à l’implémen-
tation 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 sont 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 ap-
proches « 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 sec-
tion 2.2.5, est une extension du modèle des objets distribués CORBA pour supporter le développe-
ment 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’ap-
proche à composants,
2.2. Présentation détaillée des principales approches à composants 19
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 ap-
proches 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 ap-
proches Fractal, SOFA ou encore ArchJava qui sont des approches hybrides intégrant des concepts
issus des ADLs. Finalement, il est possible de trouver une comparaison détaillée des principaux ADL
dans [Medvidovic et Taylor, 2000].
2.2.2 (D)COM(+)
Le modèle
COM (Component Object Model) [Microsoft, 1995; Rogerson, 1997] fut créé par Microsoft en 1995
afin de faire évoluer l’environnement MS-Windows vers les composants. COM est l’une des premières
solutions techniques allant dans le sens du développement par composants dont la spécification for-
melle a été proposée par la suite [Ibrahim, 1998]. COM a connu de nombreuses évolutions notam-
ment DCOM (Distributed COM) qui prend en compte l’aspect distribué des applications ou encore
COM+ qui facilite l’intégration de COM et de Java.
COM est une spécification permettant de créer des entités logicielles binaires standardisées appe-
lées des composants. COM n’impose pas de contrainte sur l’implémentation des composants mais sur
le format binaire des composants. Il est ainsi possible d’implanter un composant COM en n’importe
quel langage de programmation (toutefois, les environnements de programmation tels que Microsoft
Visual C++ ou plus récemment C# [Hejlsberg et al., 2003], facilitent grandement le développement de
composants COM). L’implémentation d’un composant peut donc être une ou plusieurs classes, une
bibliothèque de procédures ou de fonctions, etc.
En toute généralité, un composant COM possède une ou plusieurs interfaces. Une interface COM
spécifie un ensemble de signatures de méthodes et possède les caractéristiques suivantes :
20 Chap 2. Synthèse comparative des approches à composants
– elle possède un identifiant unique appelé IID (Interface IDentifier), un nombre de 128 bits gé-
néré par un algorithme pseudo-aléatoire, afin d’éviter les conflits ;
– elle est immuable et toute modification comme l’ajout, la modification ou le retrait d’une si-
gnature de fonction, est impossible ; Cette contrainte facilite la gestion de différentes versions
d’une même interface puisqu’elles ont obligatoirement des identités différentes ;
– elle hérite directement ou indirectement de l’interface IUnknown ;
– elle est décrite en langage MIDL (Microsoft Interface Definition Language).
IUnknown
IX
Composant COM
CA
IY
F IG . 2.5 : Représentation graphique d’un objet COM nommé A possédant deux interfaces IX et IY
Les objets COM (entité mémoire lors de l’exécution) sont des instances de classes qui ne peuvent
être utilisés qu’à travers leurs interfaces. La figure 2.5 montre une représentation graphique d’un objet
COM. COM spécifie qu’une interface d’un objet COM doit être un pointeur (accessible par les clients)
vers une zone mémoire du composant appelée nœud interface (cf. figure 2.6). Le premier champ de
cette zone mémoire est un pointeur vers une table de pointeurs de fonction. Cette table est aussi ap-
pelée vtable car elle est très similaire à une table des fonctions virtuelles utilisée par les compilateurs
du langage C++.
IX
Pointeur vtable
F IG . 2.6 : Organisation interne d’un objet COM ayant deux interfaces fournies IX et IY
ment du fait de l’héritage entre interfaces. Ce point est à la base du modèle de communication entre
composants COM puisqu’il est toujours possible d’accéder à un composant via cette interface qui
offre notamment l’opération queryInterface qui permet la navigation entre les différentes inter-
faces fournies du composant. D’après la spécification COM, un composant peut aussi posséder des
outgoing interfaces [Microsoft, 1995] ; un tel composant est dit connectable. Il s’agit d’interfaces dé-
clarées par un composant mais pour lesquelles il est un client c’est-à-dire qu’il utilise les fonctions
de ces interfaces. C’est ainsi qu’un composant peut être connecté à des instances de composants
offrant ces interfaces (ou une interface dérivée). Toutefois, ces interfaces sont optionnelles, donc ra-
rement utilisées et un composant COM accède directement à un autre composant via l’une de ses
interfaces fournie. Le modèle COM supporte les communications synchrones (appels de méthodes)
et les communications asynchrones via l’utilisation d’interfaces fournies standardisées. Le modèle
de communication entre composants repose sur l’API (Application Programming Interface) COM qui
fournit des opérations afin que les composants soient connectés c’est-à-dire que les références sur
les interfaces soient échangées. La figure 2.7, extraite de la spécification COM [Microsoft, 1995], sché-
matise le fonctionnement de l’API COM afin de permettre les communications entre les composants.
Concrètement, cette API est très liée à l’environnement MS-Windows et à la notion de serveur COM.
Un serveur COM est un fichier binaire (Executable, Dynamic Link Library ou OCX, bibliothèque inté-
grant une vue graphique) contenant l’implémentation de composants COM. Pour chaque composant
qu’il encapsule, le serveur COM possède une fabrique (Design Pattern Factory [Gamma et al., 1995])
chargée de créer des instances du composant. Tout serveur est muni d’un identifiant (CLSID) unique
et est enregistré dans un annuaire (i.e la base de registres du système d’exploitation MS-Windows).
Grâce à cet annuaire et à l’API COM, il est possible d’accéder dynamiquement et de façon transpa-
rente à un composant COM qu’il soit distant ou non.
thode permet de meilleures performances mais nécessite de faire attention lors de l’implémen-
tation. Par exemple, la méthode de navigation entre interfaces doit cacher le fait qu’il existe un
sous-composant encapsulé et veiller aux problèmes d’identité.
(a) (b)
F IG . 2.8 : La réutilisation par containment (a) ou par agrégation (b) en COM
Un Exemple
Pour créer un composant, il faut d’abord déclarer ses interfaces en MIDL (Microsoft Interface De-
finition Language) comme l’illustre le listing 2.1. Ce langage standardise la définition des interfaces
ainsi que les types de données de base tels que short, char, double ou float mais aussi les types struc-
turés, les tableaux ou les énumérations. Cette normalisation des types permet de définir de nouveaux
types de données en MIDL afin que tous les composants puissent les manipuler indépendamment
de leur langage d’implémentation.
A partir des déclarations MIDL des outils permettent de générer le code des interfaces et le sque-
lette des implantations de composants dans un langage de programmation donné. La figure 2.2
montre un exemple de squelette C++ d’un composant CA possédant deux interfaces IX et IY. Les
interfaces sont implantées en C++ par des classes abstraites et le composant par une classe C++ hé-
ritant des classes abstraites IX et IY. Cette classe contient l’implémentation de toutes les méthodes
2.2. Présentation détaillée des principales approches à composants 23
déclarées dans IX, IY et IUnknown. Cette implémentation du composant CA en C++ permet d’obtenir
une structuration du binaire conforme à la spécification COM. Les types utilisés dans ce programme
C++ comme ULONG sont conformes à ceux standardisés par MIDL.
#include <windows.h>
class IX : IUnknown {
public:
virtual HRESULT Fx1(short a) = 0;
virtual HRESULT Fx2() = 0;
};
class IY : IUnknown {
public:
virtual HRESULT Fy1() = 0;
virtual HRESULT Fy2() = 0;
};
// Implémentation du composant CA
class CA : public IX, public IY {
public:
// Implémentation de l’interface IUnknown
virtual HRESULT QueryInterface(const IID& iid, void** ppv) {
if (iid == IID_IUnknown) {
*ppv = static_cast<IUnknown*>(this);
}
else if (iid == IID_IX) {
*ppv = static_cast<IX*>(this);
}
else if (iid == IID_IY) {
*ppv = static_cast<IY*>(this);
}
else {
*ppv = NULL ;
return E_NOINTERFACE ; // Erreur, interface non supportée
}
reinterpret_cast<IUnknown*>(*ppv)->AddRef();
return S_OK;
}
L ISTING 2.2 : Code source de l’implémentation d’un composant COM CA ayant deux interfaces IX et
IY
Une fois créé, compilé et packagé (sous la forme d’une DLL par exemple), le composant CA peut
être enregistré dans la base de registres afin que les clients COM puissent l’utiliser de manière trans-
parente. Cette inscription du composant est réalisée par un outil qui renseigne plusieurs champs lors
de cette inscription et génère notamment un identificateur de classe (CLSID) permettant aux clients
COM d’accéder à ce composant. La figure 2.3 montre le code source C++ d’un programme client uti-
lisant la fonction Fx du composant CA à travers son interface IX.
#include <windows.h>
#include "IX.h"
int main(void) {
HRESULT hr;
if ( SUCCEEDED(hr) ) {
pIX->Fx2(); // call Fx through interface IX.
pIX->Release(); // Decr reference counting for automatic deallocation
}
return 0 ;
}
L ISTING 2.3 : Code source d’un programme client utilisant le composant COM CA
Un client COM commence toujours par initialiser la librairie COM avec la fonction CoInitialize.
Il peut ensuite utiliser des fonctions particulières telle que CoCreateInstance afin de récupérer une
2.2. Présentation détaillée des principales approches à composants 25
référence sur une interface d’un composant particulier. Cette fonction interroge l’annuaire afin de
localiser le serveur COM recherché. Une des fabriques intégrées au serveur crée ensuite une instance
de composant implantant l’interface recherchée et une référence sur cette interface est retournée
au client. Avec DCOM, le serveur peut être distant et la référence renvoyée au client n’est en réalité
que l’adresse d’un proxy. Cela est complètement transparent pour le client qui utilise directement la
référence d’interface obtenue pour invoquer les fonctions dont il a besoin.
Critique
COM est un des premiers modèles de composants visant à augmenter la réutilisation de code. Ce
modèle apporte ou met en avant :
– La notion de composant binaire réutilisable indépendamment de tout langage de programma-
tion,
– La notion d’interface qui permet à un composant d’offrir différents points de vue sur lui-même,
– Un mécanisme de navigabilité entre les interfaces d’un composant grâce à l’opération
QueryInterface,
– Un mécanisme de réutilisation de code sans héritage,
– La transparence à la localisation des composants via un annuaire,
– L’efficacité car un client ayant obtenu une référence sur l’interface d’un composant peut com-
muniquer directement avec ce dernier sans autre indirection que celle dans la table des fonc-
tions virtuelles.
COM est une des premières réponses au besoin de composants logiciels réutilisables. Toutefois,
ce modèle présente les faiblesses suivantes :
– L’intégration forte à l’environnement MS-Windows et aux outils de programmation Microsoft,
– Les interfaces outgoing ne sont pas utilisées systématiquement ce qui implique que dans la
plupart des cas on ne peut pas considérer qu’il y ait une connexion entre les composants COM
puisque le client accède directement à un composant fournisseur via l’une de ses interfaces
fournies,
– Le caractère non diffusable d’un composant qui ne contient pas de documentation et qui ne
peut pas être adapté facilement,
– Le déploiement, pourtant important, n’est pas spécifié et connaît donc des solutions ad hoc
qui ont conduit au problème connu sous le nom de l’« enfer des DLL » (appelé DLL Hell) se
produisant lorsqu’une application utilise des composants COM non répertoriés dans la base
de registres MS-Windows,
– Les services non-fonctionnels tels que la persistance, la sécurité, etc. COM+ a répondu partiel-
lement à ce besoin avec Microsoft Transaction Server (MTS) qui permet de gérer automatique-
ment les transactions.
26 Chap 2. Synthèse comparative des approches à composants
2.2.3 Javabeans
Le modèle
Le modèle de composants Javabean2 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é graphi-
quement 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 pro-
prié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 Java-
bean. 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 même possible d’en définir
de nouveaux. Nous verrons dans la suite des exemples d’événements notamment relatifs aux chan-
gements de valeurs des propriétés.
Méthodes
Evénements
Propriétés Javabean reçus
Evénements
émis
un grain de café.
2.2. Présentation détaillée des principales approches à composants 27
1 - Subscribe
(addPropertyChangeListener)
Methods
PropertyChangeEvent
2 - Publish
(propertyChange)
fois que sa valeur a été modifiée. Les propriétés liées sont particulièrement utiles pour établir des
connexions entre un objet métier et ses vues afin qu’elles se mettent à jour lors de la réception des
événements notifiant les changements de valeur de la propriété métier. Cette technique permet de
bien séparer le code métier et le code de l’interface tout en garantissant la cohérence à tout instant
des informations représentées par les vues. Une propriété est dite veto (Vetoable) lorsque son bean
émet un événement à chaque fois que sa valeur va être modifiée afin de permettre aux écouteurs de
s’opposer aux modifications. On dit que les écouteurs ont un droit de veto sur les changements de
valeur d’une telle propriété. Ces propriétés sont principalement utilisées dans les systèmes concur-
rentiels, afin de prévenir certains risques d’incohérence lors d’accès en lecture et écriture simultanés.
Un bean possède également un mécanisme standard d’introspection qui fournit, à travers son
interface BeanInfo, des informations sur lui-même (ses propriétés, les différents types d’événements
écoutés et notifiés, ses méthodes). Ce mécanisme d’introspection permet de prendre connaissance de
la structure d’un Javabean pendant son exécution. Les environnements de développement proposés
par Sun comme le Bean Development Kit (BDK) ou Netbeans utilisent ce mécanisme afin de fournir
des représentations graphiques d’un Javabean (fenêtre d’édition de propriétés, connexion de beans
graphique, etc).
Un bean sur étagère se présente sous la forme d’une archive (jar) contenant l’implémentation du
bean (fichiers java compilés) et des fichiers ressources (fichiers de configuration, images, etc). Cette
archive peut alors être facilement déployée. L’exécution d’un Javabean est supportée par la machine
virtuelle Java qui joue le rôle de conteneur. Cela rend très portables les Javabeans puisqu’il existe
aujourd’hui une machine virtuelle Java pour la plupart des plateformes matérielles.
Un exemple
La programmation d’un Javabean se fait de manière analogue à celle d’une classe Java, mais cer-
tains mécanismes spécifiques sont définis selon le schéma de conception « Observateur » [Gamma et
al., 1995] et un ensemble de règles de nommage, comme illustré par le code source 2.4.
Pour chaque propriété, un bean possède un ensemble de méthodes respectant des règles de
28 Chap 2. Synthèse comparative des approches à composants
package compteurbean;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.io.Serializable;
public Counter() {
support = new PropertyChangeSupport(this);
value = 0;
}
L ISTING 2.4 : Implémentation d’un Javabean C OUNTER possédant une propriété liée nommée Value
2.2. Présentation détaillée des principales approches à composants 29
nommage permettant d’accéder et modifier la valeur de cette propriété. S’il s’agit d’une pro-
priété mono-valuée, il faut définir les méthodes PropertyType get<PropertyName>() et void
set<PropertyName>(<PropertyType>) comme c’est le cas pour la propriété Value. Dans le cas des
propriétés multi-valuées, les conventions sont différentes puisqu’elles ajoutent un index pour dési-
gner un élément de la collection.
Le mécanisme de notification d’événement suit le patron « Observateur ». Pour chaque type
d’événements qu’il émet, un Javabean possède ainsi une méthode d’abonnement (dont le nom suit
la convention de nommage add<EventName>Listener), une méthode de désabonnement (dont le
nom suit la convention de nommage remove<EventName>Listener). Il peut aussi définir une mé-
thode de notification des écouteurs ou bien comme dans cet exemple utiliser directement la mé-
thode firePropertyChange de l’objet support qui facilite l’implémentation des événements Java-
beans. La notification consiste à instancier la classe d’événements, l’initialiser avec les informations
adéquates et transmettre cet événement aux écouteurs. Dans cet exemple, une instance de la classe
PropertyChangeEvent est créée (dans l’implémentation de méthode firePropertyChange qui n’est
pas montrée dans le listing précédent) et initialisée avec l’identité de l’objet émetteur, le nom de la
propriété dont la valeur a changé ainsi que son ancienne et sa nouvelle valeur.
Pour chaque type d’événements qu’il peut recevoir, la classe du bean implémente une inter-
face sous-type de EventListener. Dans le listing 2.5, la classe MyLabel implémente l’interface
PropertyChangeListener afin de recevoir les événements signalant les changements de valeur de
propriétés et implémente le(s) méthode(s) de traitement de ce type d’événements définies par cette
interface c’est-à-dire la méthode PropertyChange.
package mygui;
import java.io.Serializable;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeEvent;
L ISTING 2.5 : Implémentation d’un Javabean M Y L ABEL pouvant recevoir des événements de type
PropertyChangeEvent
La connexion entre deux beans peut être établie par une tierce entité c’est-à-dire en dehors de
l’implémentation de ces deux beans. Le code source 2.6 montre le code d’un programme principal
établissant la connexion entre deux Javabeans Counter et CounterView.
Un bean peut donc être connecté à n’importe quel autre bean sans aucune modification de leurs
implémentations pourvu qu’ils émettent et écoutent des événements de types compatibles. On peut
30 Chap 2. Synthèse comparative des approches à composants
L ISTING 2.6 : Programme principal établissant une connexion entre deux composants Javabeans
même s’affranchir de cette dernière contrainte grâce à la génération automatique d’un adapteur.
C’est d’ailleurs le mode de fonctionnement de l’environnement BDK (Bean Development Kit) pro-
posé en 1997. Pour établir une connexion, un adapteur est généré. Cet adapteur est spécifiquement
conçu pour recevoir les événements émis par le bean émetteur et déclencher un traitement sur le
bean écouteur. En fait, c’est l’adapteur généré qui est enregistré en tant qu’écouteur du bean émet-
teur. A chaque réception d’un événement, l’adapteur peut envoyer un message au bean récepteur.
Critique
Le modèle
1. La couche cliente (client tier) qui comprend les clients légers (thin client) et les clients lourds
(fat clients) exécutés par les utilisateurs. Un client léger est un ensemble de pages web générées
2.2. Présentation détaillée des principales approches à composants 31
servlet DB
Conteneur
EJB
Serveur Web DB
HTTP
Client Léger JSP
Transaction
(Web) Sécurité
HTML
Persistance
Depuis la version 2.0 de la spécification des EJB, la structure externe d’un EJB (cf. figure 2.12)
comprend deux paires d’interfaces. La première paire correspond à des interfaces pour les commu-
nications distantes (Remote) et la seconde pour les communications locales (Local). Chaque paire
est constituée :
– d’une interface maison permettant sa configuration lors du déploiement, l’accès à ses méta-
données, ainsi que la gestion du cycle de vie de ses instances (création, destruction, recherche
en cas de persistance, etc.),
32 Chap 2. Synthèse comparative des approches à composants
– d’une interface métier (ou fonctionnelle) spécifiant les méthodes qu’offre une instance de cet
EJB à ses clients.
Ainsi, un EJB peut offrir des services différents à ses clients locaux (s’exécutant sur la même ma-
chine) et distants. De plus, les communications via les interfaces locales bénéficient d’une implé-
mentation plus efficace puisque la gestion du réseau n’est pas prise en compte.
ILocalHome ILocalBusiness
IRemoteHome
EJB
IRemoteBusiness
Il existe trois sortes de beans : les beans session (session beans), les beans entités (rebaptisés entity
classes à partir de la version 3.0) et les beans orientés messages (message-driven beans).
Une instance d’un bean session ne peut être utilisée simultanément que par un seul client. Ce
type d’EJB permet d’intégrer des fonctionnalités spécifiques à certains clients dans la couche métier.
Il existe deux sortes d’EJB session : ceux sans état (stateless) et ceux avec état (stateful). Un bean ses-
sion sans état fournit donc un ensemble de services qu’un client invoque indépendamment les uns
des autres. Sans état signifie que le bean ne sauvegarde aucun état entre deux requêtes d’un client et le
serveur d’EJB peut alors gérer des réserves d’instances (pools) afin d’optimiser au mieux les requêtes
des clients. Un bean session avec état est souvent dédié à l’accomplissement d’un traitement spé-
cifique nécessitant plusieurs étapes. Par exemple, un bean WebOrder pourrait fournir des méthodes
permettant d’ajouter et retirer des éléments d’un panier, de valider le panier et enfin de procéder au
paiement. Ce bean session avec état ne doit communiquer qu’avec un seul client et maintenir l’état
du panier.
Les beans entités peuvent être utilisés simultanément par plusieurs clients, les problèmes de
concurrence étant gérés automatiquement par le conteneur du bean. Ce type de beans permet de
représenter des concepts métiers (e.g une personne, un compte) dont l’état peut être stocké dans une
base de données soit automatiquement pour les beans entité CMP (Container-Managed Persistency),
soit explicitement par le programmeur pour les beans entités BMP (Bean-Managed Persistency).
Les beans orientés messages n’ont pas d’état et sont dédiés à la réception et au traitement de
messages asynchrones délivrés par l’API JMS (Java Message Sevice). Ce type de beans, introduit dans
la version 2.0 de la spécification EJB, ne présente pas la même structure que les beans sessions et les
beans entités. En effet, ces beans ne possèdent pas d’interface maison, ni d’interface métier et ne sont
d’ailleurs jamais accédés directement.
2.2. Présentation détaillée des principales approches à composants 33
Un exemple
Les listings 2.7, 2.8 montrent l’implémentation d’un EJB Compte. L’interface maison définit la si-
gnature d’une méthode de création de l’EJB Compte (cf. ligne 3 du listing 2.7) et la signature d’une
méthode de recherche d’instance existante en fonction d’un identifiant (cf. ligne 7 du listing 2.7).
L’interface métier de cet EJB (cf. ligne 11 du listing 2.7) définit les méthodes fournies par une ins-
tance telles que : getNumero, getSolde ou getNomTitulaire. Ces méthodes sont déclarées comme
pouvant lever des exceptions définies dans la spécification EJB comme RemoteException signalant
les problèmes de réseaux ou CreateException signalant les problèmes d’instanciation de l’EJB. Il
est aussi possible de définir ses propres exceptions comme nous l’avons avec OperationImpossible
signalant qu’une opération sur un compte n’est pas réalisable.
La classe abstraite CompteImpl constitue une « partie » de l’implémentation de l’EJB entité
Compte. Le reste de l’implémentation d’un EJB est généré par des outils lors de son empaquetage
et de son déploiement en fonction des paramètres spécifiés dans son descripteur de déploiement.
Par exemple, le code des accesseurs (setNumero, getNumero, etc.) sera généré en fonction du mode
de persistance choisi. De même le code de la méthode create déclarée dans l’interface maison de
3 La version 3.0 de la spécification a grandement simplifié le code nécessaire à l’implémentation d’un EJB grâce aux
L ISTING 2.7 : Définition des interfaces distantes maison et métier d’un EJB C OMPTE
l’EJB n’est pas défini dans sa classe d’implémentation. Une partie de l’implémentation de cette mé-
thode est fournie par la définition de la méthode ejbCreate. Les méthodes déclarées dans l’interface
EntityBean permettent au programmeur du composant de définir ces méthodes et ainsi d’effectuer
des traitements particuliers au cours du cycle de vie de l’EJB. Par exemple, la méthode ejbActivate
sera invoquée lors du transfert en mémoire d’une instance.
Le descripteur de déploiement de l’EJB Compte est présenté sur le listing 2.9. Il s’agit d’un fichier
XML (heureusement généré par des outils) décrivant d’une part les caractéristiques générales du
composant et d’autre part son assemblage avec le serveur J2EE en spécifiant ses besoins du point
de vue des services non-fonctionnels (transaction, sécurité, etc.).
Le listing 2.10 montre le code source d’une application cliente de l’EJB Compte. Pour qu’un pro-
gramme client (distant ou non) utilise cet EJB Compte, il doit obtenir une référence vers son interface
maison afin de rechercher ou créer des instances. Le programme obtient cette référence en interro-
geant le service de nommage (Java Naming Directory Interface, JNDI, dans le cas de J2EE). Un tel
service permet, d’une part, l’enregistrement d’une ressource distribuée (ici un EJB) dans un annuaire
sous un identifiant et d’autre part de rechercher une ressource à partir d’un identifiant.
2.2. Présentation détaillée des principales approches à composants 35
// obtention d’une référence sur un EJB Compte à travers son interface maison
Object ref = ctx.lookup("java:comp/env/ejb/EJBCompte");
try{
// création d’une instance de l’EJB Compte
Compte compte = compteHome.create( new Integer(numcpt), "toto", 1000000 );
Critique
Le modèle
CORBA (Common Object Request Broker Architecture) est une norme définie par l’OMG4 , dont
l’objectif est de fournir des mécanismes permettant le développement d’applications distribuées en
s’affranchissant des problèmes de communication, d’hétérogénéité, d’intégration et d’interopéra-
bilité [Object Management Group, 2002]. Les premières normes CORBA ont spécifié un intergiciel
pour objets distribués qui supporte l’interopérabilité pour des objets distants et hétérogènes (au sens
des langages de programmation et des systèmes d’exploitation). La version 1.0 de la norme, datant
de 1999, s’appuie sur le modèle orienté objet (héritage, encapsulation et polymorphisme) et l’archi-
tecture client/serveur. Cette version intègre le langage OMG IDL (Interface Definition Language) qui
permet de décrire les interfaces des objets distribués et utilise le protocole générique GIOP (General
Inter-ORB Protocol) pour les communications distantes, dont l’instanciation la plus utilisée est IIOP
(Internet Inter-ORB Protocol) qui repose sur le protocole TCP/IP (Transmission Control Protocol/Inter-
net Protocol). Toutefois, les aspects de diffusion, de déploiement, et de construction d’une application
complète ne sont pas abordés et c’est dans cette optique que la norme Corba 3.05 a introduit un mo-
dèle de composants explicite nommé CCM (Corba Component Model) [Object Management Group,
2002] qui est une évolution structurelle des objets distribués Corba. Les composants s’exécutent au
sein d’un conteneur qui s’exécute lui-même au sein d’un serveur (de même que dans le modèle EJB).
Les communications entre les conteneurs s’effectuent via le bus CORBA qui offre aussi un accès aux
services non-fonctionnels.
Un composant CCM encapsule sa représentation interne et son implémentation pour n’offrir à
ses clients qu’une surface opaque. La figure 2.14 présente les éléments structurels d’un composant
CCM. Un composant est constitué d’attributs permettant sa configuration et de ports chacun étant
associé à une interface décrite en IDL. On distingue quatre sortes de ports : facettes (interfaces of-
fertes), les réceptacles (interfaces requises), les sources d’événements et les puits d’événements qui sont
des interfaces événementielles.
Un composant CCM offre ses services à travers ses facettes. Elles permettent à différents clients
de disposer de l’interface dont ils ont besoin sur un composant et seulement de celle-ci. L’interface
associée à une facette indique les services (méthodes) fournis. Un service peut appartenir à plusieurs
facettes. Il est admis qu’un client change de point de vue sur un composant et donc change de fa-
cette, pour cela, il utilise les opérations de navigation entre les multiples facettes qui sont offertes par
la référence de base du composant. Il s’agit d’une facette qui fournit un point d’entrée vers les possi-
bilités d’un composant (navigation entre les ports, accès aux attributs, aux fonctionnalités, etc.). Un
composant possède aussi une facette de fabrique (Component Home) fournissant les opérations de
gestion des instances comme pour les EJB.
Les réceptacles permettent à un composant d’exprimer ses dépendances vis-à-vis d’autres com-
posants fournisseurs de services via leurs facettes. Facettes et réceptacles sont donc les points de
connexions complémentaires des composants CCM. Un réceptacle peut accepter une ou plusieurs
référence(s) sur des facettes de composants suivant qu’il est multiple ou simple. Lorsqu’un compo-
sant reçoit sur l’un de ses réceptacles une référence, cela lui permet d’utiliser toutes les fonctionnali-
tés offertes par le composant (ou l’interface) désigné par cette référence. C’est-à-dire qu’il peut invo-
quer un service ou s’abonner aux événements levés par ce dernier via ses sources d’événements. Deux
opérations de base sont fournies sur un réceptacle : la connexion et la déconnexion. La connexion
d’une facette et d’un réceptacle (ou plusieurs dans les cas multiples) suppose une compatibilité de
leurs interfaces c’est-à-dire que l’interface de la facette soit un sous-type de l’interface du réceptacle.
Les sources et les puits d’événements sont les homologues respectifs des facettes et des récep-
tacles dédiés aux communications asynchrones par événements entre les composants. Une source
d’événements indique qu’un composant émet un certain type d’événements. Un puits d’événements
est rendu public par un composant afin de recevoir des événements d’un certain type en provenance
d’un ou plusieurs producteurs. De même que précédemment, suivant l’arité de la source d’événe-
ments, un ou plusieurs clients peuvent y souscrire. Les connexions entre une source et un (ou plu-
sieurs) puits sont aussi soumises à la contrainte du sous-typage c’est-à-dire que l’interface de la
source doit être un sous-type de l’interface du puits. Ce mécanisme événementiel est à rapprocher
de celui du modèle Javabeans.
40 Chap 2. Synthèse comparative des approches à composants
On distingue quatre sortes de composants CCM : les composants service (sans état), les com-
posants sessions (état non persistant), les composants process (état persistant mais pas de référence
unique accessible) et les composants entity (état persistant et référence unique accessible).
Un exemple
Les interfaces des composants sont décrites en utilisant le langage OMG IDL issu des précédentes
normes CORBA dédiées aux objets distribués. De même que pour MIDL (cf. section 2.2.2), le langage
OMG IDL a pour objectif de masquer l’hétérogénéité en décrivant de façon standardisée les interfaces
des objets distribués. OMG IDL standardise ainsi le format binaire des types de données de base, la
définition de nouveaux types de données, la déclaration des opérations, etc. La déclaration d’une
opération est (entre autres) constituée de son nom, du type du résultat, de la liste de ses paramètres,
chacun possédant un type et un mode de passage (in, out, inout) ainsi que de la liste des exceptions
qu’elle peut lever. En résumé OMG IDL est un langage de description d’interfaces standardisé com-
plet qui permet l’interopérabilité entre les objets ou les composants communiquants via l’intergiciel
CORBA.
Dans la suite, nous présentons un exemple adapté de [Riveill et Merle, 2000]. Le listing 2.11
montre la définition en IDL 3.0 d’un composant D ISTRIBUTEUR. Le composant Distributeur a trois
facettes : fClient, fFournisseur et fDepanneur. La première est destinée aux utilisateurs du dis-
tributeur et fournit des méthodes telles que l’insertion de la monnaie ou le choix de la boisson. La
deuxième est destinée aux fournisseurs et fournit des méthodes permettant d’ouvrir le distributeur,
d’ajouter des matières premières ou de vider le monnayeur. Et enfin la troisième s’adresse aux dépan-
neurs qui peuvent ouvrir le distributeur mais aussi remplacer des pièces.
Le listing 2.11 montre une description du composant D ISTRIBUTEUR complètement indépen-
dante de toute implémentation. C’est le CIF (Component Implémentation Framework) qui défi-
nit la manière d’implémenter un composant. Le CIF inclut notamment le langage CIDL (Com-
ponent Implementation Description Language) permettant de décrire des implémentations de com-
posants et de leurs maisons. Le listing 2.12 montre un exemple de déclaration CIDL qui précise des
choix d’implantation du composant comme sa catégorie (ici entity) et le nom des deux classes C++
MaisonDistributeurImpl et DistributeurImpl contenant son implémentation (que nous ne dé-
taillons pas ici).
module MonApplicationDistributeurImpl {
composition entity DistributeurImpl {
home executor MaisonDistributeurImpl {
implements MonApplicationDistributeur::MaisonDistributeur;
manages DistributeurImpl;
};
};
};
Le CIF fournit aussi des outils de génération de code permettant de générer des implémentations
partielles (squelettes de code) de composants à partir de descriptions faites en CIDL (Component
2.2. Présentation détaillée des principales approches à composants 41
component Distributeur {
provides FacadeClient fClient; // Une facette
provides FacadeFournisseur fFournisseur;
provides FacadeDepanneur fDepanneur;
Implémentation Description Language). Ces implémentations partielles peuvent être générées dans
de nombreux langages de programmation car les règles de projection entre les concepts OMG IDL et
CIDL et les concepts de la plupart des langages de programmation ont été définies. Par exemple, en
C++, un composant CCM peut être implanté par deux classes : l’une pour la fabrique de composants
(Maison) et l’autre pour l’implémentation des facettes du composant.
Un composant CCM est ensuite empaqueté sous la forme d’un fichier archive (.zip). Cette archive
contient les fichiers de l’implémentation du composant et plusieurs fichiers XML :
– un descripteur CSD (CORBA Software Descriptor) décrivant le logiciel (auteurs, licence, compi-
lateur utilisé, etc.),
– un descripteur CCD (CORBA Component Descriptor) décrivant les caractéristiques techniques
du composant (modèle de transaction, de qualité de service, etc.),
– un descripteur CPF (Component Property File) indiquant des valeurs par défaut des attributs
des composants,
– un descripteur CAD (Component Assembly Descriptor) décrivant les caractéristiques d’assem-
blage de plusieurs composants (contraintes de connexions, etc).
Le listing 2.14 montre un exemple de fichier CAD décrivant la connexion du puits nommé cAlert
d’un composant L IGHTA LARM avec la source pVide d’une instance du composant D ISTRIBUTEUR.
2.2. Présentation détaillée des principales approches à composants 43
<componentassembly id="IL300707">
<description>
Exemple d’assemblage de composants
</description>
<componentfiles>
<componentfile id="file_distrib">distributeur.zip</componentfile>
<componentfile id="file_la">lightalarm.jar</componentfile>
</componentfiles>
<partitioning>
<homeplacement id="home_distrib">
<componentfileref idref="file_distrib" />
<componentinstanciation id="aDistrib" />
</homeplacement>
<homeplacement id="home_la">
<componentfileref idref="file_la" />
<componentinstanciation id="aLa" />
</homeplacement>
</partitioning>
<connections>
<connectevent>
<consumesport>
<consumesidentifier>cAlert</consumesidentifier>
<componentinstanciationref idref="aLa" />
</consumesport>
<publishesport>
<publishesidentifier>pVide</publishesidentifier>
<componentinstanciationref idref="aDistrib" />
</publishesport>
</connectevent>
</connections>
</componentassembly>
Critique
2.2.6 Fractal
Le modèle
Fractal [Bruneton et al., 2002] est un modèle abstrait de composants dont la spécification a été
proposée par le consortium Object Web8 en 2002. Cette spécification connaît actuellement deux im-
plémentations de références : Julia9 en Java et Cecilia10 en C/C++, et d’autres implémentations expé-
rimentales comme FractTalk11 en Smalltalk ou encore FractNet 12 en .NET. Un langage de description
d’interfaces (IDL) basé sur XML est aussi proposé en Fractal et il peut être traduit en Java ou en OMG
IDL.
Un composant Fractal, représenté sur la figure 2.2.6 (issue de [Passama, 2006]), est constitué d’un
contenu (partie implémentation) encapsulé par une membrane (partie contrôle).
Le contenu d’un composant contient l’implémentation de ses fonctionnalités métiers et peut être
constitué d’un nombre fini d’autres composants appelés dans ce cas sous-composants. Le modèle
Fractal est récursif (on utilise aussi le terme hiérarchique) et permet donc la construction de compo-
sites (aussi appelés super-composants du point de vue de leurs sous-composants) à partir de compo-
6 http://openccm.objectweb.org
7 http://cadena.projects.cis.ksu.edu
8 http://www.objectweb.org
9 http://fractal.objectweb.org/julia/index.html
10 http://fractal.objectweb.org/c.html
11 http://csl.ensm-douai.fr/FracTalk/
12 http://www-adele.imag.fr/fractnet/
2.2. Présentation détaillée des principales approches à composants 45
sants. Fractal propose aussi le partage (sharing) qui permet à un composant d’appartenir à plusieurs
composites.
La membrane d’un composant comprend les aspects non-fonctionnels relatifs à l’interaction,
l’introspection et l’intercession du composant. Elle est constituée d’interfaces qui sont les points d’ac-
cès au composant. Les interfaces possèdent trois caractéristiques que nous désignons par les termes
suivants :
– « visibilité », une interface externe est située sur la paroi extérieure de la membrane ; elle est donc
accessible et utilisable par des clients externes au composant alors qu’une interface interne
n’est utilisable que par son contenu ;
– « orientation », une interface serveur est une interface fournie à travers laquelle le composant
offre un ensemble d’opérations et une interface cliente est une interface requise à travers la-
quelle le composant émet des invocations de services ;
– « rôle », une interface de contrôle standardise les opérations permettant de gérer les aspects
non-fonctionnels, les liaisons ou le cycle de vie d’un composant ou de son contenu, alors
qu’une interface fonctionnelle décrit les opérations métiers du composant.
Ces trois caractéristiques peuvent se combiner. Par exemple, une interface serveur fonctionnelle
et externe déclare un ensemble d’opérations offertes par le composant. Une interface serveur externe
de contrôle permet d’offrir des opérations permettant de gérer le composant, il existe d’ailleurs un
ensemble standard d’interfaces de contrôle :
– AttributeController permet de modifier la valeur d’un attribut du composant,
– ContentController permet de consulter et modifier le contenu d’un composant (qui n’est
donc pas une boîte noire) notamment en ajoutant ou retirant des sous-composants,
– BindingController permet de connecter et déconnecter les sous-composants d’un compo-
46 Chap 2. Synthèse comparative des approches à composants
site,
– LifeCycleController permet d’arrêter ou démarrer un composant par exemple.
Un composant ne possède pas forcément toutes ces interfaces. C’est ainsi que la spécification
classe les composants en différentes catégories : les composite components qui exposent leur contenu,
les primitive components qui n’exposent pas leur contenu mais possèdent au moins une interface de
contrôle et les base components qui ne possèdent aucune interface de contrôle. Les composants de
base permettent d’arrêter la récursion du modèle Fractal et ils peuvent être considérés comme des
objets possédant plusieurs interfaces fonctionnelles [Bruneton et al., 2004].
L’assemblage de composants dans le modèle de composants Fractal repose sur la liaison (bin-
ding) d’interfaces. Il existe plusieurs trois sortes de liaisons primitives :
– la liaison normale (normal binding) lie une interface cliente externe à une interface serveur
externe,
– la liaison d’export (export binding) lie une interface cliente interne d’un composite à une inter-
face externe serveur d’un de ses sous-composants,
– la liaison d’import (import binding) lie une interface cliente externe d’un sous-composant à
une interface interne serveur d’un de ses super-composants.
Les liaisons composites désignent un chemin de communication établi à partir d’un ensemble
de liaisons primitives et composants de liaison (binding components) comme des talons (stubs), des
squelettes (skeletons) ou des adapteurs (adapters). Ces composants de liaison sont aussi appelés
connecteurs en Fractal [Bruneton et al., 2002].
Fractal définit aussi un système de types pour les interfaces et les composants. Le type d’une
interface de composant comprend : son nom, son language type (le type de l’élément définissant l’in-
terface dans un langage de programmation donné), son rôle (cliente ou serveur) mais aussi sa contin-
gence (obligatoire ou optionnelle) et sa cardinalité (singleton ou collection). Ces deux dernières ca-
ractéristiques sont particulièrement utiles pour la validation des connexions et plus généralement la
vérification d’architectures. Le type d’un composant est défini comme l’ensemble des types de ses
interfaces. La relation de sous-typage entre les types des interfaces et ceux des composants repose
sur la substituabilité. On peut résumer les règles de sous-typage ainsi [Bruneton et al., 2002] :
– Le type d’une interface I 1 est sous-type du type d’une interface serveur (resp. cliente) I 2 si :
– le nom de I 1i est le même que celui de I 2 ,
– l’interface de langage correspondante à I 1 est une sous-interface (resp. super-interface) de
celle correspondante à i 2 ,
– si I 2 est obligatoire (resp. optionnelle) alors I 1 est obligatoire (resp. optionnelle) aussi,
– si I 2 a une cardinalité collection alors I 1 a une cardinalité collection aussi.
– Le type d’un composant C 1 est sous-type d’un composant C 2 si et seulement si chaque type
des interfaces clientes de C 1 est sous-type d’un type d’interface de C 2 et que chaque type des
interfaces serveurs de C 1 est sous-type d’un type d’interface de C 2 .
Ces règles définissent d’une part les liaisons valides en Fractal et d’autre part les substitutions
de composants autorisées. Toutefois, la déclaration des types n’est pas obligatoire en Fractal et le
programmeur peut décider d’utiliser le système de types ou non en fonction de ses besoins (rien n’est
obligatoire en Fractal, la spécification définit d’ailleurs plusieurs niveaux de conformance en fonction
2.2. Présentation détaillée des principales approches à composants 47
Un exemple
Fractal ne fait aucune supposition relative au langage de description d’interfaces (IDL) à utili-
ser. Julia, l’implémentation de référence en Java, intègre un ADL utilisant une syntaxe Java. Toutefois,
un autre ADL a été spécifié afin d’être indépendant des langages et purement déclaratif en utilisant
une syntaxe XML. Il existe aussi un outil permettant de créer graphiquement des descriptions d’ar-
chitectures et de générer du code Julia. La figure 2.2.6 montre un exemple d’architecture issu de la
spécification Fractal [Bruneton et al., 2004]. Il s’agit d’un serveur web nommé Comanche constitué
d’un ensemble de composants tels que le Receiver qui reçoit les requêtes HTTP ou encore le Logger
qui journalise les requêtes reçues.
C BC CC LC
C BC CC LC C BC CC LC
Scheduler C BC CC LC
C LC
C LC
Dispatcher File
Frontend
C BC LC
C LC
Error
Handler
Backend
Comanche
Le listing 2.15 montre une partie de la description XML de l’architecture de cette application.
Critique
Fractal est sans conteste une contribution majeure dans le domaine des composants. Ce mo-
dèle abstrait est indépendant de toute technologie. Le concept de contrôleur est une explicitation du
conteneur dans les modèles EJB et CCM. Le fait qu’un composite soit un composant permet de dé-
crire et de manipuler simplement des architectures à différents niveaux d’agrégation. Cela constitue
une réponse au problème du passage à l’échelle évoqué dans la section 2.1.2.
Néanmoins, le modèle Fractal n’intègre pas explicitement la notion de port. Elle est en réalité
complètement intégrée dans la notion d’interface. Une interface est ainsi considérée en Fractal à la
fois comme un point de connexion d’un composant et comme un contrat fonctionnel. Cela provoque
bien souvent des ambiguïtés même si la spécification attire quelque peu l’attention sur la différence
entre une interface de composant et une interface de langage (au sens Java). De plus, le support des
connecteurs via les binding components semble assez primitif et peu étudié contrairement à d’autres
approches comme SOFA.
48 Chap 2. Synthèse comparative des approches à composants
<definition name="example.comanche.Logger">
<interface name="l" signature="example.comanche.Logger" role="server"
cardinality="singleton" contingency="mandatory"/>
</definition>
<definition name="example.comanche.BackendType">
<interface name="rh" signature="example.comanche.RequestHandler" role="server"/>
</definition>
<definition name="...">
...
</definition>
2.2.7 SOFA/DCUP
Le modèle
SOFA (SOFtware Applicances) [Plásil et al., 1998] est un projet, né en 1998, dont l’objectif est de
fournir une plate-forme pour le développement d’applications par « composition » d’un ensemble
de composants. DCUP (Dynamic Component UPdating) propose une architecture et une extension
du modèle de composants SOFA afin de supporter le versionning, mais aussi le téléchargement et la
mise à jour de façon « sûre » et dynamique (pendant l’exécution de l’application) de composants.
En SOFA, une application est vue comme un emboîtement de composants décrit par un descrip-
teur de déploiement. Un composant est instance d’un template (ou component type). Un template est
décrit par le frame et l’architecture de ses instances (cf. figure 2.2.7). Le frame est une vision boîte noire
d’un composant définissant ses interfaces requises et fournies. De façon optionnelle, un frame peut
déclarer des propriétés permettant de configurer le composant. Un frame peut être implémenté par
plusieurs architectures. Une architecture est soit une implémentation opaque dans le cas de compo-
sants primitifs, soit une description structurelle comprenant les instanciations des sous-composants
et la mise en place de liaisons dans le cas de composites.
2.2. Présentation détaillée des principales approches à composants 49
composite
partie contrôle partie permanente
partie remplaçable
CM
CB
interface
interface requise
fournie
sous- rôle
partie fonctionnelle connecteur rôle requis
composant fourni
SOFA distingue quatre sortes de liaisons : (i ) les liaisons entre une interface requise et une inter-
face fournie, (i i ) les liaisons dites de délégation entre une interface fournie d’un composite et une
interface fournie d’un de ses sous-composants, (i i i ) les liaisons dites de subsomption (subsuming)
entre une interface requise d’un sous-composant et une interface requise de son composite, (i v)
les liaisons d’exemption (exempting) qui correspondent aux interfaces non-utilisées. Les trois pre-
miers types de liaisons sont réalisées par des connecteurs. Un connecteur implémente la sémantique
de l’interaction entre les composants et couvre les besoins d’adaptation entre les composants. Les
connecteurs sont aussi considérés comme une solution au problème de l’anomalie de déploiement
(deployment anomaly problem [Bálek et Plásil, 2001]). Par exemple, choisir un mode de communica-
tion distant (Remote Procedure Call par exemple) entre deux composants A et B lors du déploiement
nécessiterait normalement de modifier leurs interfaces. L’utilisation d’un ou plusieurs composants
adapteurs n’est pas une solution envisageable puisqu’elle implique de modifier l’architecture d’un
éventuel composite C englobant A et B à cause d’une contrainte de déploiement. Afin de répondre à
ce problème, les connecteurs sont des entités de première classe en SOFA. Chaque type de connec-
teur implémente la sémantique d’un type d’interaction. Un connecteur est décrit de façon similaire
à un composant : par un frame de connecteur et une architecture de connecteur. Le frame d’un
connecteur est représenté par un ensemble d’instances de rôles. Un rôle est une interface générique
de connecteur prévue pour être liée à une interface de composant. L’architecture d’un connecteur
décrit le contenu du connecteur. De même que pour un composant, il peut être simple (il contient
seulement des éléments primitifs et est directement implémenté dans l’environnement sous-jacent)
ou composé (il contient seulement des instances d’autres connecteurs ou composants). Les connec-
teurs les plus simples, exprimant les appels de procédures (CSProcCall), sont implicites et ne doivent
pas être spécifiés dans l’architecture. SOFA fournit d’autres types de connecteurs prédéfinis comme
EventDelivery et DataStream. L’utilisateur peut même définir ses propres connecteurs.
50 Chap 2. Synthèse comparative des approches à composants
Le langage de description d’architecture CDL comporte aussi la possibilité d’attacher des des-
criptions comportementales sous la forme de protocoles [Plásil et al., 1999; Plásil et Visnovsky, 2002]
à différents niveaux de description d’un composant (frame, architecture et interface). Un protocole
est un formalisme à base d’expressions régulières permettant de décrire les invocations d’opérations
entrantes (vers le composant) et sortantes (vers un composant connecté) un peu comme des traces.
La conformité des protocoles est basée sur l’inclusion des traces.
L’extension DCUP de SOFA propose une extension du modèle de composants SOFA afin de sup-
porter la mise à jour des composants de façon sûre pendant leur exécution. Un composant DCUP (cf.
figure 2.2.7) est divisé en deux parties : une permanente et une remplaçable. C’est la partie rempla-
çable d’un composant qui peut être changée dynamiquement et c’est aussi cette partie qui fait l’objet
d’un contrôle de versions. DCUP introduit deux entités de contrôle : le component manager (CM) et
le component builder (CB). CM est le cœur de la partie permanente et est responsable du contrôle
du cycle de vie du composant pendant l’exécution. Chaque partie remplaçable (et versionnée) d’un
composant est associée à un CB qui est responsable du contenu d’un composant, c’est-à-dire des
sous-composants pour un composite ou des objets d’implémentation pour un composant primitif.
Un CB gère la création, l’initialisation, le stockage et la restauration de l’état d’un composant avant et
après une mise à jour.
SOFA définit le langage CDL (Component Definition Language). The langage, basé sur OMG IDL,
permet de décrire les interfaces, les frames et les architectures des composants et des connecteurs.
Les descriptions CDL sont compilées et stockées dans le TIR (Type Information Repository). Le TIR
gère l’évolution des descriptions de composants et stocke les différentes versions de chaque élément
défini en CDL. Dans le TIR, les éléments versionnés sont référencés par leur nom et une spécification
de version. Dans les descriptions CDL, un développeur peut faire référence à une version spécifique
présente dans le TIR.
Un exemple
L’exemple présenté dans cette section est adapté de [Plásil et al., 1998]. Il s’agit de la réalisation
d’un composant composite Bank (cf. listing 2.16). Le frame du composant B ANK (cf. ligne 26 du lis-
ting 2.16) montre qu’il possède une interface multiple clients et une interface supervisor. Un
client peut réaliser les actions définies dans l’interface IClient alors qu’un superviseur pourra réa-
liser celles définies dans l’interface ISupervisor. La ligne 9 du listing 2.16 montre un exemple très
simple de protocole associé à l’interface IClient. Ce protocole indique l’ordre autorisé pour les opé-
rations définies dans l’interface, à savoir d’abord createAccount, ensuite (la séquentialité est indi-
quée par ;) les opérations deposit, withdraw et balance sont réalisables dans n’importe quel ordre
(le choix est indiqué par +) et autant de fois que nécessaire (indiqué par *) pour finir par l’opéra-
tion deleteAccount une seule fois. L’architecture Bank montre que ce composant est un composite
et décrit les instanciations de ses sous-composants s et d s et leurs liaisons. Le mot clef local est ici
utilisé pour lier les interfaces client à l’implémentation de l’architecture B ANK. De même que pour
les précédentes propositions, des outils permettent de générer le code des interfaces, des component
builders, des squelettes d’implémentation (en Java, en C++, etc.) et des descripteurs de déploiement
(en XML).
2.2. Présentation détaillée des principales approches à composants 51
1 module BankDemo {
interface IClient {
3 string createAccount(in float init);
string deleteAccount(in string account);
5 boolean deposit(in string account, in float amount);
boolean withdraw(in string account, in float amount);
7 boolean balance(in string account);
protocol:
9 createAccount; (deposit + withdraw + balance)*; deleteAccount
};
11 interface ISupervisor {
string assignAccountName();
13 boolean canWithdraw(in string account);
boolean canDelete(in string account);
15 };
interface IAccount {
17 float getBalance();
void setBalance(in float balance);
19 };
interface IDataStore {
21 IAccount create(in string account);
IAccount load(in string account);
23 void delete(in string account);
};
25
frame Bank (property: num_of_clients) {
27 provides:
IClient client[num_of_clients];
29 ISupervisor supervisor;
requires:
31 IDataStore storeAccess;
};
33 };
Critique
2.2.8 ArchJava
Le modèle
ArchJava [Aldrich et al., 2002b; Aldrich et al., 2002a] a été créé en 2001 avec la volonté d’inté-
grer les concepts apportés par les ADLs comme Darwin [Magee et Kramer, 1996], dont il est inspiré,
dans un langage de programmation à objets en l’occurrence Java. Les auteurs d’ArchJava ont constaté
que les ADLs favorisent une meilleure conception des architectures logicielles, facilitant leur com-
préhension et autorisant des approches formelles pour leur analyse. Cependant, les ADLs utilisent
souvent leur propre langage qui ne permet généralement pas de produire une application exécu-
table. Ce découplage dans les approches existantes entre la description d’une architecture et son
implémentation ne permet pas d’assurer la non-violation des propriétés architecturales dans l’im-
plémentation. C’est par exemple le cas pour l’intégrité des communications [Moriconi et al., 1995;
Luckham et Vera, 1995] qui spécifie que dans une implémentation, un composant ne doit commu-
2.2. Présentation détaillée des principales approches à composants 53
niquer qu’avec ceux avec lesquels il est connecté dans l’architecture. L’objectif d’ArchJava est donc
d’étendre le langage à objets Java afin d’unifier la description d’une architecture logicielle et son
implémentation en garantissant que l’implémentation est conforme aux contraintes architecturales
définies. ArchJava étend le langage Java en introduisant des concepts architecturaux et un système
d’annotation de types nommé AliasJava.
Du point de vue architectural, ArchJava a introduit la notion de classe de composant (component
class) qui décrit des composants assemblables afin de construire des architectures (cf. figure 2.2.8).
Une classe de composant peut contenir des déclarations de ports, de méthodes et d’attributs (au
sens Java), de composants encapsulés dits sous-composants (sub-components) et de connexions. Un
composant est une instance d’une classe de composant qui communique avec d’autres composants
via ses connexions. Les extrémités d’une connexion sont des ports qui sont les points de communi-
cation des composants. Un port déclare un ensemble de méthodes fournies (provides) et requises
(requires). Une méthode fournie est implémentée dans la classe du composant et rendue accessible
aux composants connectés à ce port. Inversement, une méthode requise via un port est fournie par
un composant connecté via ce port.
composite connexion
(connect) sous-composant
port compiler
connexion
(glue)
Les annotations de types d’AliasJava ont pour objectif de contrôler statiquement (à la compila-
tion) les échanges de références faits dans une implémentation pour garantir l’intégrité des com-
munications. AliasJava a introduit les annotations suivantes : unique (une donnée référencée par une
seule variable), owned (une donnée appartenant à un domaine défini par un unique objet et accessible
par l’ensemble des objets de ce domaine uniquement), shared (une donnée partagée par les objets
du programme comme un singleton [Gamma et al., 1995] par exemple) et lent (souvent utilisé pour
les paramètres ou les variables locales, une référence vers une donnée lent ne peut pas être stockée
par ailleurs). Ces annotations peuvent être vues comme une généralisation des modificateurs Java
pour les droits d’accès. Le listing 2.17 montre un exemple (adapté de [Aldrich et al., 2002b]) de code
ArchJava et des annotations.
La primitive connect permet de lier un ensemble de ports qui sont bidirectionnels (mélange de
méthodes requises et fournies) en ArchJava. Par exemple, pour lier les ports p 1 , p 2 , ..., p n appartenant
respectivement aux composants c 1 , c 2 , ..., c n , il faut écrire :
La sémantique de cette instruction est la suivante : une méthode requise intervenant dans la
connexion est liée à une (s’il en existe plusieurs, il s’agit d’un cas d’erreur) méthode fournie de si-
gnature compatible (au sens du sous-typage).
Pour spécifier des composants ayant plusieurs connexions avec un même type d’entités — par
exemple, le composant B ANK (cf. section 2.2.7) qui est connecté à plusieurs composants C LIENT —
ArchJava introduit la notion de port interface. Un port interface est instancié dynamiquement en un
port à chaque fois qu’il est utilisé pour établir une connexion. Le port dynamiquement créé joue
alors pleinement son rôle de point d’accès du composant. Comme ArchJava vérifie statiquement la
validité d’une architecture, il est nécessaire de déclarer dans le code source des patrons de connexion
(connect pattern) qui spécifient les connexions dynamiques.
ArchJava propose aussi un mécanisme d’héritage pour les classes de composants. Une classe de
composant peut hériter d’une autre classe de composant ou de la classe Java standard. Lorsqu’une
classe de composant hérite d’une classe Java standard, l’intégrité des communications n’est plus as-
surée par le compilateur ArchJava. Toutefois, ce mode dégradé permet la compatibilité avec les API
Java existantes. Lorsqu’une classe de composant hérite d’une autre classe de composant, la sous-
classe de composant hérite de tout ce que possède sa super-classe : ses méthodes, ses ports et ses
connexions. Il est de plus possible :
– d’ajouter des attributs, des méthodes, des ports, des sous-composants et des connexions,
– de redéfinir des méthodes,
– d’ajouter des méthodes fournie à un port hérité.
Par contre, afin de préserver la substituabilité, il est interdit d’ajouter des méthodes requises à un
port hérité. Dans tous les cas, les règles Java s’appliquent et il est impossible par exemple d’hériter la
classe de composant C OMPILER et d’essayer de redéfinir les sous-composants en les remplaçant par
des versions plus spécifiques puisque Java ne supporte pas la covariance.
Un exemple
L’exemple développé dans cette section est adapté de [Aldrich, 2003]. La figure 2.2.8 montre l’ar-
chitecture d’un composite W EB S ERVER et le listing 2.18 montre le code de ce composite. Dans cet
exemple, le sous-composant ROUTER accepte des requêtes HTTP et les transmet à un W ORKER qui les
traite. A chaque nouvelle requête entrante, le ROUTER demande un nouveau W ORKER (cf. ligne 29) à
travers son port request pour traiter la demande. Ce port request est connecté au port privé create
du composite W EBSERVER (cf. ligne 3). Dans l’implémentation de la méthode requestWorker du port
create, une nouvelle instance de W ORKER est créé et connectée à travers son port serve (cf. ligne 11).
Cette connexion dynamique est valide car conforme au pattern de connexion déclaré à la ligne 4.
L ISTING 2.18 : Code source des composants W EB S ERVER, ROUTER et W ORKER en ArchJava
2.2. Présentation détaillée des principales approches à composants 57
Critique
ArchJava est l’une des rares approches à tenter d’intégrer dans l’implémentation les descriptions
architecturales normalement décrites par les ADLs. Cela a pour objectif de faciliter la compréhension
des programmes, de garantir l’intégrité des communications entre les composants grâce au système
de type AliasJava et d’assurer une bijection entre le code et l’architecture durant les phases de mainte-
nance ou d’évolution de l’application. Un autre atout d’ArchJava est certainement le fait qu’il est une
extension de Java dont la syntaxe est largement connue ce qui permet un apprentissage rapide [Ben-
nouar et al., 2006] et contribue à son essor comparé à d’autres propositions bien plus lourdes à mettre
en œuvre.
ArchJava souffre de quelques faiblesses du point de vue de la dynamicité. Tout d’abord, l’intégrité
des communications est vérifiée statiquement, ce qui impose de déclarer à l’avance des patrons de
connexions réduisant ainsi les possibilités à l’exécution. Par ailleurs, il n’est pas possible d’enlever
des connexions. ArchJava n’offre aucune possibilité pour décrire les aspects comportementaux d’une
architecture comme c’est le cas en SOFA avec les protocoles. Le contrôle de l’intégrité des commu-
nications par le typage impose des contraintes de programmation avec l’utilisation d’annotations de
types qui complexifient l’écriture et la lecture du code (comme le prouve l’exemple développé ci-
dessus). Nous pensons que la principale faiblesse d’ArchJava est justement d’être une extension de
Java. En effet, cela suppose que tous les concepts et mécanismes objets (de Java) sont compatibles
avec l’objectif visé. En ArchJava, les objets et les composants supportent des contraintes différentes.
Par exemple, une référence sur un objet peut être échangée entre composants ou objets lors de l’exé-
cution alors qu’une référence sur un composant ne peut pas, justement pour éviter de violer l’inté-
grité des communications. Cela se traduit par le fait que tous les paramètres des méthodes sont obli-
gatoirement définis par une classe Java standard comme c’était le cas pour InputStream ou Token
dans les exemples présentés. Dans ce contexte, comment décider ce qui doit être un objet et ce qui
doit être un composant lors de la conception d’une application ? Et cette décision ne sera-t-elle pas
décisive pour les futures évolutions de l’application ?
Le modèle
UML (Unified Modeling Language) [OMG, 2004] est un langage graphique normalisé, défini par
l’OMG, permettant la modélisation de systèmes. Dans sa version 1.x, ce langage intégrait déjà la no-
tion de composants, considérés comme des unités modulaires, déployables et interchangeables d’un
système, encapsulant l’implémentation et n’exposant qu’un ensemble d’interfaces. Cette vision assez
bas niveau des composants pour un langage de modélisation a changé à partir d’UML 2.0 [Chees-
man et Daniels, 2000]. Les composants sont maintenant des unités de structuration abstraites qui
représentent des sous-parties d’un système, pouvant être modélisées selon différents points de vue
et raffinées tout au long du cycle de développement.
UML 2.0 a introduit des concepts et mécanismes, inspirés des ADLs, permettant de décrire les
systèmes en terme de composants interconnectés. Un composant (Component) est une extension de
la notion de classe structurée (StructuredClass), elle-même étant une extension indirecte (via Struc-
58 Chap 2. Synthèse comparative des approches à composants
interface fournie
Plusieurs ports peuvent être définis pour un composant, ce qui permet de distinguer des interac-
tions différentes selon le port à travers lequel elles sont réalisées. Les ports renforcent le découplage
entre un composant et son environnement afin qu’il puisse être réutilisé dans un autre environne-
ment conforme aux contraintes imposées sur le port. Les contraintes associées à un port peuvent
être des interfaces fournies, des interfaces requises ou des protocoles. Un port peut être associé à
plusieurs interfaces fournies et plusieurs interfaces requises définissant ainsi l’offre et la demande
correspondant à une collaboration à travers ce point de communication du composant. Les inter-
faces décrivent des contraintes statiques (nature et signature des opérations) qui doivent faire l’objet
d’une implémentation par un classifier (classe ou composant). Les protocoles sont des descriptions
de contraintes dynamiques qui peuvent s’effectuer à l’aide de diagramme d’états dont nous donnons
un exemple dans la suite.
Les collaborations entre deux ou plusieurs composants se traduisent par des connecteurs repré-
sentant les possibilités de communication entre plusieurs instances de composants. Il existe deux
sortes de connecteurs en UML : les connecteurs d’assemblage (assembly connector) et les connecteurs
de délégation (delegation connector). Un connecteur d’assemblage permet de relier des composants
qui fournissent et requièrent des services compatibles. Un connecteur d’assemblage est défini entre
une interface requise (resp. un port requis) et une interface fournie (resp. port fourni). Les connec-
teurs de délégation sont utilisés pour la construction de composites, c’est-à-dire de composants
ayant une structure interne constituée de parts (des Property qui dérivent de TypedElement) et de
connecteurs. Les connecteurs de délégation permettent de relier les contrats externes d’un compo-
sant (interfaces ou ports) aux contrats externes de ces parts afin que de rediriger les requêtes d’opé-
rations entrantes. De même que pour les ports, un connecteur peut être décrit par un protocole et il
est ensuite possible de vérifier la validité d’une connexion en vérifiant la conformité des protocoles.
2.2. Présentation détaillée des principales approches à composants 59
Un exemple
La figure 2.2.9 présente l’exemple d’un composite S TORE tiré de la spécification de UML [OMG,
2004]. Ce composite est constitué de trois sous-composants (parts), respectivement dédiés à la ges-
tion des ordres de commandes (O RDER), des produits (P RODUCT) et des clients (C USTOMER). Ce compo-
site contient aussi quatre connexions : deux connexions de délégation et deux connexions d’assem-
blage. On peut remarquer que dans cet exemple, les interfaces requises et fournies sont directement
attachées à un composant et non à un port. Cela provient du fait que la notion de composant est dé-
finie en UML comme une extension de celle de classe. Cela est assez déroutant sachant que les ports
des composants devraient être leurs seuls points d’interactions.
Critique
A partir de UML 2.0, un pas vers la description d’architecture a été franchi par rapport aux versions
précédentes. Les concepts de port, de connecteur et de diagramme d’architecture ont été introduits.
UML permet aussi bien les descriptions structurelles des architectures que les descriptions compor-
tementales via les protocoles notamment. La plupart des ADLs ont les mêmes objectifs que UML à
savoir la modélisation des systèmes. Depuis sa version 2.0, UML est bien mieux adapté pour décrire
60 Chap 2. Synthèse comparative des approches à composants
OrderEntry {protocol}
addOrderLine
create
InProcess
Validated
validateDetails
des architectures logicielles qu’il ne l’était dans ses versions précédentes comparé aux ADLs [Medvi-
dovic et al., 2002]. Actuellement, non seulement il supporte les principales constructions des ADLs
mais il offre aussi un mécanisme d’extension (sous la forme de profil) puissant qui permet de l’adap-
ter lorsqu’il n’offre pas en standard un concept ou un mécanisme dont on pourrait avoir besoin.
UML souffre de plusieurs défauts dont le principal est sa complexité. Il est en effet difficile de
maîtriser complètement les mécanismes UML (surtout les plus récents) qui manquent souvent de
précisions dans la spécification. C’est ainsi que nous avons montré que des interfaces peuvent être
attachées directement à un composant alors qu’elles devraient toujours être attachées à un port d’un
composant. UML souffre comme la plupart des ADLs d’un découplage fort avec l’implémentation.
Dans cette section, nous comparons les différentes approches à composants et notamment celles
étudiées en détails dans la section précédente.
Focalisation
(D)COM(+) Réutilisation dans l’environnement MS-Windows
Javabeans Assemblage graphique (à la souris) de Javabeans
EJB Développement d’applications Web selon une architecture multi-
niveaux
CCM Développement d’applications réparties et hétérogènes à l’aide de
composants
Fractal Modèle hiérarchique et récursif de composants supportant le partage
de sous-composants
ArchJava Extension de Java garantissant l’intégrité des communications entre les
composants dans l’implémentation
SOFA Mise à jour dynamique des composants et connexion de composants
via des connecteurs avec vérification de la compatibilité comporte-
mentale basée sur la notion de protocole
UML Conception d’applications à l’aide de composants (extension des clas-
sifiers) éventuellement composites et de connexions
Wright Description et analyse formelles du comportement des composants,
des connecteurs et des configurations (descriptions d’assemblages de
composants via des connecteurs)
Domaine
d'application
Spécialisé
EJB (3-tiers)
Javabeans (événnements)
ADLs COM (~Windows)
CCM (distribué)
(Wright, ...) Lagoona
Sofa ArchJava
UML Fractal ComponentJ
Généraliste
Niveau
d'abstraction
Spécification Description Code source Code-octet
formelle déclarative Binaire
F IG . 2.23 : Niveau d’abstraction et domaine d’application : deux axes de classification des approches
à composants
62 Chap 2. Synthèse comparative des approches à composants
Les ADLs sont des approches qui peuvent être aussi bien généralistes que spécialisées, par contre
elles s’intéressent principalement à la spécification des architectures. Depuis sa version 2.0, UML
s’inscrit dans la même veine que les ADLs mais il ne propose pas de support formel contrairement à
de nombreux ADLs. Les modèles de composants Fractal et SOFA sont assez généralistes et indépen-
dants de l’implémentation. CCM est plus orienté pour le développement d’applications distribuées.
Les Javabeans et les EJB sont des approches plus spécialisées car elles sont très liées à l’environ-
nement Java. De plus, les EJB se focalisent essentiellement sur les applications web construites en
couches. Les approches ArchJava, ComponentJ ou encore Lagoona sont des langages de program-
mation offrant au programmeur des facilités (primitives, structures de contrôle, etc.) pour mettre en
pratique la programmation par composants. Finalement, COM est la proposition qui ne s’intéresse
qu’au format binaire des composants bien que cela ait changé depuis l’avènement de .NET.
Le tableau 2.2 présente une synthèse des différentes approches étudiées jusqu’ici. Les critères de
comparaison classés par thème sont les suivants :
– La structure :
– Itf. fournies. Un composant offre-t-il une ou plusieurs interfaces qui définissent différents
points de vue ?
– Itf. requises. Un composant exprime-t-il ses dépendances externes notamment sous la forme
d’une ou plusieurs interfaces requises ?
– Ports. Un composant possède-t-il des ports qui constituent ses points d’interaction et de
connexion ?
– Composites. Le modèle propose-t-il la notion de composite c’est-à-dire de composant qui
encapsule d’autres composants interconnectés ?
– Connecteurs. Le modèle réifie-t-il la notion de connexion ?
– Le code source :
– Desc. d’itf. En quel langage sont écrites les descriptions d’interfaces ?
– Desc. de comp. En quel langage sont écrites les descriptions de composants ?
– Implémentation. En quel langage de programmation (LP) sont implémentés les composants ?
– Le déploiement :
– Unité déployée. Sous quelle forme sont diffusés les composants ?
– Automatisation. Le modèle propose-t-il un support automatisé ou semi-automatisé du dé-
ploiement ?
– Description. En quel langage est décrit ou configuré le déploiement ?
– Serv. non-fonct.. Est-il possible de configurer des services non-fonctionnels pour un compo-
sant ?
– L’exécution :
– Environnement. Quel est l’environnement d’exécution des composants ?
– Connexion. Est-il possible de connecter dynamiquement des composants ?
– Déconnexion. Est-il possible de déconnecter dynamiquement des composants ?
– Remplacement. Est-il possible de remplacer dynamiquement un composant ?
(D)COM(+) Javabeans EJB CCM Fractal SOFA ArchJava UML
a
Itf. fournies n ! 4 n n n n n
Itf. requises ! ! ! n n n n n
Ports ! ! ! Unidir.b ! ! Bidir. Bidir.
Structure
Composites " ! ! ! " " " "
Connecteurs ! ! ! ! !c " " "
Desc. d’itf. MSIDL Java Java OMG IDL IDLd IDL ArchJava UML
Desc. de comp. LPe Java Java CIDL ADLf CDL ArchJava UML
Code
Source
g
Implémentation Squel.+LP Java Java LP Squel.+LP Squel.+LP ArchJava /
Unité déployée Dll, Exe Jar Jar, Ear, War Zip ! inconnueh ! /
Automatisation ! ! " " ! " ! /
Description ! ! XML XML ! XML ! Diag.
Déploiement
! ! " " ! !
2.3. Comparaisons des approches à composants
Exécution
Remplacement " /
Légende : " : supporté ! : non supporté / : non pertinent <vide> : non spécifié mais possible n : arité multiple Diag. : Diagrammes
On peut remarquer que les modèles industriels se focalisent en premier lieu sur des probléma-
tiques pragmatiques telles que le déploiement, le packaging ou encore les services non-fonctionnels.
Ils manquent pour la plupart d’un réel modèle de composants comme c’est le cas pour COM, Ja-
vabeans ou EJB. Avec le modèle de composants CCM, l’OMG fait le pont entre les problématiques
pragmatiques et les concepts apportés par les ADLs. Fractal est un modèle de composants réflexif
qui met en avant les notions de composites (représentation des configurations dans les ADLs par des
composants) et de partage. SOFA se concentre sur la dynamicité avec le remplacement dynamique
de composants ou encore sur les connecteurs qui sont la réification des connexions entre les com-
posants. ArchJava est une extension du langage Java afin d’exprimer l’architecture des applications
directement dans le code source et ainsi assurer l’adéquation entre les descriptions architecturales et
l’implémentation.
Dans la section 2.1.1, nous avons présenté la notion de découplage. Nous nous concentrons dans
cette section sur les approches permettant l’implémentation effective des composants. Nous avons
aussi intégré Java dans cette comparaison afin de comprendre où se situent les langages à objets.
Le tableau 2.3 présente une comparaison des différentes approches en regard de cette propriété de
découplage. Pour cela, nous distinguons les critères suivants :
Code côté fournisseur Un composant fournissant un service possède-t-il du code spécifique (par
exemple la gestion des écouteurs en Javabeans), écrit par son programmeur et relatif aux
connexions ou aux communications afin que ce service puisse être utilisé ?
Code côté client Un composant client utilisant un service externe possède-t-il une référence ex-
plicite sur le composant fournisseur ? Le client possède-t-il du code spécifique relatif aux
connexions ou aux communications afin de pouvoir utiliser un service externe ? Par exemple,
les modèles EJB et (D)COM(+) imposent d’intégrer du code dans le composant client afin de
rechercher une référence sur l’interface maison du composant fournisseur dans un annuaire.
Changer le fournisseur Est-il possible de changer dynamiquement le fournisseur d’un service pour
un composant client ?
Découplage C’est la synthèse du tableau qui propose 3 niveaux de découplages (faible, moyen et
élevé) que nous établissons en fonction des deux derniers critères précédents. En effet, le pre-
mier critère (code dans le fournisseur) n’est pas une limitation en terme de couplage (seule-
ment une contrainte pour le programmeur) puisque le fournisseur ne possède jamais de réfé-
rences « figées » sur ses clients.
(D)COM(+) et EJB sont les approches les moins satisfaisantes du point de vue du découplage.
CCM et ArchJava sont moyennes puisqu’elle ne permettent pas le changement dynamique de four-
nisseur dans leur versions de base. Les approches les plus pertinentes en terme de découplage sont :
Javabeans, Fractal et SOFA. Javabeans est une approche particulière dont la communication repose
entièrement sur le schéma de conception Observateur. Cela permet en effet un bon niveau de dé-
couplage mais impose des contraintes fortes sur la programmation des composants. Fractal et SOFA
2.3. Comparaisons des approches à composants 65
TAB. 2.3 : Niveau de découplage des composants dans les différentes approches
Légende : o : absence de code spécifique • : présence de code spécifique
" : supporté ! : non supporté ∼ : partiellement supporté
sont donc les propositions les plus abouties dans ce domaine et l’approche SOFA se démarque encore
grâce au support des connecteurs.
2.3.5 Assemblage
L’assemblage de composants est un mécanisme central qui repose dans la plupart des modèles
sur différents types de liaisons entre les ports (ou les interfaces pour les modèles sans ports). Le ta-
bleau 2.4 propose une synthèse en ce qui concerne l’assemblage entre les composants. Ce tableau ne
prend pas en compte les propositions (D)COM(+), EJB et Javabeans car ces approches ne présentent
pas les mécanismes comparés. Nous utilisons les critères de comparaison suivants :
– Type de liaisons. Quels sont les différents types de liaisons supportés ?
– On distingue généralement deux niveaux de conformités :
– Syntaxique. Comment est vérifiée la validité syntaxique des liaisons ?
– Comportementale. Comment est vérifiée la validité comportementale des liaisons ? Ce ni-
veau de vérification relève de la sémantique des liaisons et vise à garantir la robustesse dyna-
mique des communications effectuées via une liaison.
– Types de donnés échangés. Que peuvent s’échanger les composants comme données ?
a Un objet client doit intégrer le code lui permettant de posséder une référence sur un objet fournisseur (instanciation,
accesseurs)
b Si l’on a utilisé des accesseurs dans le client (cf. figure 2.2), le changement dynamique est possible
c Possible si le client a été doté d’interfaces outgoing ou peut-être en modifiant directement une entrée dans la base de
registre Windows
d Le code de gestion des Observateurs (abonnement, désabonnement, signalement)
e La génération automatique d’un adapteur permet de s’affranchir de code spécifique du côté client (souscripteur)
f Interrogation de l’annuaire JNDI, etc.
g Peut-être en modifiant directement une entrée de l’annuaire JNDI
Chap 2. Synthèse comparative des approches à composants
Dans les approches supportant les composites, on distingue généralement les liaisons de déléga-
tion entre un composite et l’un de ses sous-composants et les liaisons « normales » entre deux com-
posants. CCM distingue aussi les liaisons de types requis/fourni (communication synchrone) et les
liaisons événementielles (communication asynchrone). ArchJava proposait aussi dans ses premières
versions la notion de service diffusé (broadcast) qui s’apparentait à des liaisons événementielles
(bien qu’il s’agissait de communications synchrones aussi).
La vérification des connexions entre les composants repose dans la majorité des modèles sur
la relation de sous-typage entre les interfaces pour la conformité syntaxique et sur les protocoles
pour la conformité comportementale. Les protocoles peuvent être décrits à l’aide de formalismes va-
riés comme on a pu le constaté en SOFA et en UML. ArchJava propose aussi un système de typage
plus complet pour contrôler l’intégrité des communications entre les composants. Cela passe par un
contrôle des données échangées entre les composants afin d’assurer l’intégrité des communications.
Les approches à composants sont souvent sous-spécifiées en ce qui concerne les types de données
échangeables et cela se traduit par des choix propres à chaque implémentation. Par exemple, l’im-
plémentation Julia de Fractal permet de passer en paramètre n’importe quel objet Java ce qui peut se
traduire par la violation de l’intégrité des communications lors de l’exécution [Léger et al., 2006].
2.3.6 Synthèse
Après avoir étudié toutes ces propositions, nous pensons que le critère du découplage est ce-
lui qui est fondamental dans l’approche composants. En effet, le découplage favorise d’une part la
réutilisation des composants dans divers contextes et d’autre part la dynamicité puisque ces liens de
couplage explicites peuvent être modifiés à tout moment. En regard de ce critère, nous proposons le
classement suivant :
– approches à objets (distribuées) : EJB, (D)COM(+)
– approches à composants : Javabeans, CCM, Fractal, SOFA, ArchJava, UML 2.0
Les EJB et les composants COM ne constituent pas, selon nous, des approches à composants du
fait que le code de l’interaction entre un composant client et un composant fournisseur est encore
noyé dans le code métier. Nous pouvons résumer cela par la phrase suivante qui résume notre vision
de la différence principale entre l’approche à objets et l’approche à composants :
En POO, lors d’un envoi de message, un objet appelant détient une référence sur un objet rece-
veur. Suivant la manière dont cette référence est acquise (statiquement ou dynamiquement)
et suivant si elle peut être changée dynamiquement, elle peut constituer un lien de couplage
fort qui réduit la potentialité de réutilisation de l’objet appelant.
En POC, un composant client ne détient pas de référence sur un composant fournisseur mais
ils sont mis en relation par une connexion modifiable (lien de couplage faible), rendue possible
grâce à l’explicitation des dépendances requises.
68 Chap 2. Synthèse comparative des approches à composants
2.4 Conclusion
La première famille consiste à définir des normes ou standards de programmation qui permet-
tront la réutilisation et l’assemblage des composants. Par exemple, Javabeans impose de respecter
des conventions syntaxiques telles que l’utilisation d’accesseurs pour définir des propriétés ou en-
core l’utilisation du schéma de conception Observateur pour permettre l’assemblage des Javabeans.
Le respect de ces conventions incombe au programmeur qui ne peut s’en affranchir sous peine de res-
treindre les réutilisations potentielles de son composant et même rendre impossible son assemblage.
(D)COM(+) repose sur un standard binaire ce qui permet de rendre les composants indépendants de
tout langage de programmation. Toutefois, un programmeur doit choisir un compilateur générant
du binaire conforme à la norme et doit même dans certains cas écrire son code source de telle sorte
que le compilateur génère du binaire conforme. Cela impose une bonne connaissance du fonction-
nement du compilateur. L’arrivée de .NET a quelque peu pallié cet inconvénient puisque n’importe
quel langage conforme à la CLS (Common Language Specification) possède un compilateur vers du
code-octet (bytecode) conforme.
La seconde famille impose de programmer les composants en étendant un framework comme
c’est le cas pour les EJB. Cela rend l’implémentation des composants dépendante du framework en
question et du langage dans lequel ont été développés les composants. Toutefois, l’implémentation
des composants est facilitée par rapport à la première famille puisque le programmeur est déchargé
d’une grande quantité de code à écrire.
La troisième famille concerne particulièrement les approches qui se veulent indépendantes de
tout langage de programmation. Les interfaces et les composants sont décrits à l’aide d’un langage
déclaratif standardisé tel que OMG IDL ou SOFA CDL. Des squelettes d’implémentation des compo-
sants peuvent ensuite être générés grâce à des outils de génération de code utilisant ces descriptions.
Le programmeur doit ensuite compléter ces squelettes.
La dernière famille comprend des langages de programmation qui proposent des abstractions
et/ou des mécanismes afin de programmer des composants mais aussi des applications par assem-
blage de composants. Par analogie avec les langages à objets, nous appelons ces approches des lan-
gages à composants.
Seuls les langages à composants proposent de supporter le paradigme composant au niveau de
l’implémentation. Bien que la seconde approche tente de s’abstraire des langages d’implémenta-
tion, il nous semble que ceux-ci sont importants puisqu’ils déterminent en grande partie la facilité
2.4. Conclusion 69
de maintenance, de compréhension et de rétro-ingénierie du code source (cf. figure 2.4). Utiliser des
langages de programmation qui n’offrent pas un bon niveau de découplage, ni un réel support de la
POC, est tout à fait possible mais cela se traduit inévitablement par la complexification de toutes les
activités se basant sur le code source. Cela conduit à un éloignement entre la conception et l’implé-
mentation puisque les abstractions utilisées sont différentes. Cela impose bien souvent l’utilisation
de conventions (schémas de conception ou encore règles syntaxiques), partiellement prises en charge
par des outils de génération de code.
Implémentation
Génération de code
Tests
Maintenance
Modèles de Langages de
ADLs
composants programmation
Rétro-Ingénierie
Cycle de
développement
Les langages à composants ont donc pour objectif principal d’offrir un support au niveau du lan-
gage de programmation pour faire de la POC. Ces langages proposent des concepts de haut niveau
spécifiques pour l’approches à composants, ce qui facilite l’implémentation des composants mais
aussi la génération de code ou encore la rétro-ingénierie du code source écrit dans ces langages. De
plus, le test mais aussi la maintenance (évolutions mineures) sont des phases importantes du cycle
de développement qui s’effectuent au niveau du code source. Les langages à composants sont donc
indispensables à l’essor du développement par composants.
Les principaux langages à composants existants sont actuellement construits par extension de
langages à objets existants comme ArchJava ou ComponentJ qui sont des extensions du langage Java.
Or, les langages à objets ne sont peut-être pas vraiment adaptés pour faire de la programmation par
composants comme cela est souligné dans [Fröhlich et Franz, 1999; Beugnard, 2005]. Il semble que
peu de travaux se soient intéressés à vraiment construire un langage à composants en se concentrant
sur les problématiques au cœur de cette approche. Nous pouvons citer par exemple les travaux sur le
langage Lagoona [Fröhlich et al., ; Fröhlich et al., 2005] qui est un langage dans lequel le concept de
classe a été revu et qui introduit deux nouveaux mécanismes : stand-alone messages et generic mes-
sage forwarding. Bien que ce langage soit intéressant, il ne permet pas de décrire une architecture lo-
gicielle en terme de composants interconnectés comme on pourrait le faire en ArchJava par exemple.
Lagoona ne s’est pas assez inspiré, selon nous, des récents travaux sur les approches à composants.
C’est dans ce contexte que nous proposons dans le chapitre 3 une étude visant à définir les abs-
tractions et mécanismes de base d’un langage à composants. Notre objectif est de construire un lan-
gage à composants en nous concentrant en premier lieu sur les problématiques centrales de ce mode
de développement à savoir le découplage et l’assemblage.
3
CHAPITRE
Spécification de S CL :
un langage à composants minimal
Préambule
Ce chapitre présente le cœur du travail de cette thèse qui est la spécification du langage S CL (Simple
Component Language) [Fabresse et al., 2007a; Fabresse et al., 2007b]. Nous commençons par présen-
ter les motivations et les objectifs qui ont guidé la conception de S CL. Ensuite, chaque section est une
discussion autour d’un besoin de la programmation par composants afin d’y répondre au mieux dans
S CL. Les choix faits sont ainsi discutés et argumentés tout au long de ce chapitre. Finalement, après
une synthèse du noyau de S CL, ce chapitre se termine par un bilan de ce qui a été réalisé en mettant en
exergue les spécificités de S CL.
71
72 Chap 3. Spécification de S CL : un langage à composants minimal
A
U delà des discours et de l’existence de différents modèles et langages, la programmation par
composants (PPC) est encore peu répandue et nous disposons de peu d’expérimentation de
sa pratique. Notre but premier est d’examiner point par point les besoins du programmeur afin de
déterminer quels doivent être les traits essentiels d’un langage à composants (LAC), ceux qui sont
accessoires voire inutiles ou nuisibles.
Cette volonté de créer un nouveau langage de programmation pour faire de la programmation par
composants (PPC) est née de plusieurs constats. Le premier est que les langages de programmation
traditionnels (procéduraux ou objets) ne sont pas adaptés à la PPC et pourtant utilisés pour mettre en
pratique ce mode développement. Ils imposent aux programmeurs de respecter des conventions ou
des schémas de conception afin d’implémenter les concepts de l’approche composant en utilisant
ceux du langage de programmation utilisé. Par exemple, la programmation d’un composant Java-
beans, s’effectue avec le langage Java en respectant des conventions de nommage et en appliquant
le schéma de conception Observateur [Gamma et al., 1995] (cf. chapitre 2). Cela complexifie l’implé-
mentation mais aussi le test, la maintenance ou encore l’évolution du code source de l’application qui
ne profite pas directement et simplement des avantages apportés par l’approche composant. Notre
deuxième constat concerne les langages de programmation tels que ArchJava, ComponentJ, Lagoona
ou Piccola. Bien que ces langages intègrent effectivement des abstractions et des mécanismes spé-
cifiques pour faire de la PPC, ils ne présentent pas tous les mêmes alors qu’ils utilisent souvent un
vocabulaire commun. Cette disparité invite à mieux identifier et définir les besoins de la PPC.
Finalement, on peut résumer ces problématiques soulevées par ces deux constats par les ques-
tions générales suivantes : Qu’est-ce qu’un langage à composants ? Quelles sont ses structures de
données et de contrôles fondamentales ?
Tout d’abord, en accord avec les objectifs de cette thèse et la synthèse des approches à compo-
sants présentés respectivement dans les chapitres 1 et 2, nous posons la définition suivante :
Cette définition générale a pour but de fixer les idées sur la signification de la notion de « langage
à composants » (LAC) dans cette thèse. Un LAC est destiné à être utilisé à la fois par un programmeur
de composants et par un architecte d’application (cf. chapitre 1). Parmi les approches à composants
actuelles, seules certaines sont conformes à cette définition comme ArchJava, ComponentJ, Piccola
ou encore Lagoona. En effet, tous ces langages permettent de faire de la PPC et proposent de nou-
velles structures de données et de contrôle à cet effet contrairement aux propositions qui utilisent
principalement Java comme Julia (l’implémentation de référence de Fractal) qui est un framework
écrit en Java.
3.1. Motivations et Objectifs 73
Dans la suite de ce chapitre nous décrivons S CL (Simple Component Language) qui est notre pro-
position de langage à composants. Avec S CL, nous souhaitons proposer un langage à composants le
plus minimal possible. Notre idée est de ne pas intégrer un mécanisme (notamment objet) s’il n’est
pas indispensable au développement par composant. On peut énoncer plus précisément cet objectif
de la façon suivante :
Objectif 1 Proposer un langage à composants qui intègre les concepts et mécanismes nécessaires et
suffisants pour faire de la programmation à base de composants.
Ce premier objectif souligne notre volonté de minimalité afin de produire un langage simple et
cohérent. Cette idée est bien exprimée par une phrase de Saint-Exupéry : « Il semble que la perfection
soit atteinte non quand il n’y a plus rien à ajouter, mais quand il n’y a plus rien à retrancher »1 . Nous
ne prétendons pas cependant vouloir atteindre la perfection.
Notre second objectif est de proposer un langage de programmation qui permette au program-
meur de s’exprimer facilement et donc de proposer des abstractions et mécanismes de haut niveau.
Comme le souligne J. Privat dans sa thèse [Privat, 2006], « un bon langage de programmation doit per-
mettre au programmeur de s’exprimer facilement, il doit donc être le plus proche possible du mode de
pensée humain ». Cela doit permettre une meilleure expressivité du langage qui est un indicateur de
la capacité des programmeurs à s’exprimer succinctement et directement. Ces propos sont résumés
par l’objectif suivant :
Objectif 2 Les concepts et mécanismes intégrés dans un langage à composants doivent être de haut
niveau et en adéquation avec l’approche composant afin de faciliter l’expressivité des programmeurs.
Une des difficultés actuelles de l’approche composant est de construire des composants réuti-
lisables dans différentes applications. Or, ils sont souvent originellement conçus pour les besoins
d’une application particulière. Cette pratique semble inévitable surtout si aucun des composants
disponibles sur étagère ne satisfait les besoins exigés. Nous pensons que cette problématique doit
être prise en compte directement au niveau des langages de programmation. En effet, c’est au lan-
gage à composants d’offrir les mécanismes permettant de définir un composant indépendamment
du contexte d’exécution pour lequel il est destiné a priori. Cette volonté de rendre les composants
indépendants de leur environnement (de définition, d’exécution au sein d’une application particu-
lière, etc.) a pour objectif de maximiser leur réutilisation potentielle. Ceci nous donne notre dernier
objectif.
Objectif 3 Un langage à composants doit garantir autant que possible l’écriture de composants indé-
pendamment de tout contexte.
Ce troisième objectif est fondamental pour comprendre les choix effectués dans S CL. Dans la
suite, on parlera de non-anticipation lors de la définition d’un composant c’est-à-dire de la néces-
sité de ne pas pré-câbler dans le code d’un composant ce qui pourrait empêcher son utilisation dans
1 Extrait du livre « Terre des hommes » paru en 1939.
74 Chap 3. Spécification de S CL : un langage à composants minimal
certains contextes. Autrement dit, tout code relatif à un contexte d’utilisation particulier, comme ce-
lui relatif aux communications avec des entités extérieures par exemple, ne doit donc pas être intégré
dans le code d’un composant.
Ces trois principaux objectifs étant posés, les sections qui suivent détaillent maintenant les choix
effectués pour S CL. Pour cela, nous avons adopté une démarche constructive où l’on identifie un à un
les besoins de la PPC et intégrons les concepts et/ou mécanismes couvrant ce besoin en s’inspirant
des propositions existantes décrites dans le chapitre 2. Nous pensons que cette démarche contribue à
remplir notre premier objectif. Dans tous les cas, l’ensemble des choix effectués lors de la conception
de S CL ont été décidés dans l’optique de remplir au mieux ces trois objectifs.
Pour discuter des concepts liés à la spécification de S CL, nous avons besoin de définir un voca-
bulaire de base. Nous définissons ce vocabulaire de base à l’aide de termes volontairement généraux
afin de ne pas introduire d’ambiguïtés avec l’existant ou de choix implicites.
Un vision imagée des composants est : « A component is a static abstraction with plugs » [Niers-
trasz et Dami, 1995]. Les prises (plugs aussi appelées hooks) d’un composant constituent ses uniques
points d’accès au même titre que les pattes d’un composant électronique.
Elles contribuent à renforcer l’encapsulation du composant qui ne peut être accédé que via
l’une de ses prises et augmentent l’indépendance des composants par rapport à leur environnement
(d’autres composants) en rendant explicite les interactions faites avec celui-ci. A travers ses prises, un
composant fournit une ou plusieurs fonctionnalités.
Pour utiliser une fonctionnalité offerte par un composant, il faut l’invoquer. Un composant peut
ne pas être directement utilisable c’est-à-dire que l’invocation d’une de ses fonctionnalités ne fonc-
tionnera pas s’il n’est pas assemblé au préalable. Nous distinguons deux mécanismes d’assemblage de
composants : la connexion et la composition qui sont respectivement détaillés dans les sections 3.7
et 3.11. De façon générale, connecter des composants consiste à lier leurs prises. Ces liaisons per-
mettent de satisfaire les dépendances des composants exprimées via leurs prises en utilisant les fonc-
tionnalités fournies par d’autres composants via leurs prises aussi. La composition de composants
consiste à construire un nouveau composant en connectant des composants existants. Ces nouveaux
composants sont dits composites car ils sont eux-mêmes constitués de composants plus élémentaires
appelés sous-composants.
Illustrons ce vocabulaire de base à travers une analogie avec le langage C. Par exemple, consi-
dérons les fichiers objets (.o) comme des composants. Les prises d’un composant sont les symboles
présents dans la table des symboles du fichier objet. On distingue les prises femelles (symbole non
3.3. Faut-il des descripteurs de composant ? 75
défini correspondant à une fonction appelée mais non définie dans un fichier .c) et les prises mâles
(symbole défini correspondant à une fonction définie dans un fichier .c). On peut voir un composite
comme un fichier de bibliothèque statique (.a) obtenu par agrégation de fichiers objet. Cette analogie
montre ici ses limites puisqu’un composite peut normalement fournir de nouvelles fonctionnalités
non présentes dans ses sous-composants. Dans cette analogie, la connexion de composants est réali-
sée par l’outil d’assemblage (linker) lorsqu’il lie chaque symbole non-défini avec un unique symbole
défini. En annexe A.1, nous montrons qu’il est possible, dans une moindre mesure et en respectant
des règles précises, d’adopter un style de programmation par composants en utilisant des langages
de programmation standards comme le C ou Java.
Dans la suite de ce chapitre, nous définissons d’une part la structure des composants en S CL
c’est-à-dire comment sont représentés leurs prises et leurs fonctionnalités, et d’autre part les méca-
nismes tels que l’invocation d’une fonctionnalité, la liaison de prises, la connexion et la composition
de composants.
Il existe deux grandes familles de langages à objets : les langages à classes (ou descripteurs) et
les langages à prototypes [Lieberman, 1986; Abadi et Cardelli, 1996]. Les langages à classes sont au-
jourd’hui très largement répandus même si des langages à prototypes sont encore utilisés dans des
domaines spécifiques comme par exemple le web avec Javascript [Flanagan, 1998]. Dans le monde
objet, les termes classe et instance désignent respectivement les descriptions d’objets dans le code et
les objets eux-mêmes lors de l’exécution. Par analogie, on peut se demander si un langage à compo-
sants doit être construit sur un modèle avec des descripteurs2 ou avec des prototypes.
Dans le monde composant, il n’existe pas deux termes bien identifiés pour désigner les descrip-
teurs et les instances. Ainsi, le terme « composant » peut aussi bien désigner un descripteur instan-
ciable qu’une instance c’est-à-dire une entité en mémoire à l’exécution. Cette ambiguïté de voca-
bulaire se traduit par des différences dans la littérature et les langages. Par exemple, un descripteur
est désigné par le terme « classe de composant » (mot-clé component class) en ArchJava alors qu’il
est désigné par celui de « composant » (mot-clé component) en ComponentJ. Ainsi, lorsqu’on parle
d’un « composant », il s’agit d’une instance d’une classe de composant en ArchJava alors qu’il s’agit
descripteur en ComponentJ.
En dehors de ce problème de vocabulaire, on constate que la plupart des langages à composants
sont construits sur un modèle à descripteurs et non sur un modèle à prototypes. Le seul exemple à
ma connaissance de langage à composants à prototypes a été proposé dans [Zenger, 2002]. Un com-
posant ne possède ni état ni identité et est créé via des primitives de raffinement de composants exis-
tants. Le composant de bootstrap est nommé component et il ne fournit et ne requiert aucun service.
Les services d’un composant ne peuvent être utilisés directement et ce dernier doit préalablement
être instancié. En effet, ce langage distingue les notions de composant et d’instance de composant. Le
vocabulaire est assez surprenant pour un langage à prototypes et témoigne du manque de précision
en ce qui concerne l’approche prototypique pourtant revendiquée. Toutefois cette proposition a le
2 On n’utilise pas le terme « classe » pour éviter toute ambiguïté avec le monde objet.
76 Chap 3. Spécification de S CL : un langage à composants minimal
mérite de soulever la question de savoir si un langage à composants peut être (ou doit être) construit
sur un modèle à prototypes puisque la plupart des langages à composants (ArchJava, ComponentJ,
etc.) sont actuellement bâtis sur un modèle à descripteurs.
Les arguments pour ou contre l’utilisation de descripteurs dans le monde des composants nous
semblent similaires à ceux avancés dans le monde des objets [Dony et al., 1992]. Les arguments don-
nés en faveur des langages à prototypes sont :
– la simplicité ; cet argument n’a été retenu dans le monde des objets puisque la plupart des LOO
sont actuellement construits sur un modèle à descripteurs et il ne semble pas non plus s’impo-
ser dans le monde des composants ;
– l’indépendance des prototypes qui ne sont pas liés à un descripteur ; cet argument ne consti-
tue pas une limitation des langages à descripteurs dans le monde des composants puisqu’un
composant est empaqueté sous la forme d’une archive contenant son descripteur notamment
(cf. chapitre 2) afin d’être facilement mis sur étagère ou déployé.
A l’inverse, la principale critique faite aux langages à prototypes est la difficulté à décrire des abs-
tractions (caractéristiques partagées par toute une famille d’objets) de façon réellement satisfaisante
bien que des solutions aient été proposées dans certains langages comme les traits en S ELF [Ungar et
Smith, 1987]. Dans l’optique de réaliser des applications de taille importante, ce manque d’abstrac-
tion et donc de structuration est une forte limitation. En S CL, nous avons donc choisi d’adopter une
approche avec descripteurs.
Choix 1 Un composant est une entité logicielle présente à l’exécution, instance d’un descripteur de
composant.
Ce choix en faveur d’une approche à descripteurs soulève deux questions. La première : Est-ce les
descripteurs ou les composants qui sont mis sur étagère et réutilisés ? Tout d’abord, rappelons qu’une
étagère est considérée ici comme une bibliothèque d’archives (fichiers jar par exemple) contenant
des entités logicielles réutilisables dans différentes architectures (applications). Dans la majorité des
approches à composants, l’archive mise sur étagère contient essentiellement un ou plusieurs des-
cripteurs. En effet, un composant étant une entité présente à l’exécution, elle peut difficilement être
mise sur étagère afin d’être réutilisée. Toutefois, le modèle Javabeans offre la possibilité d’inclure un
composant Javabeans sérialisé (enregistré sur disque) dans une archive. Cela permet notamment de
mettre sur étagère des composants initialisés (possédant des valeurs) par exemple. Dans les autres
approches, des valeurs initiales peuvent aussi être spécifiées dans un fichier de configuration inclut
dans l’archive et lu lors de l’instanciation du descripteur. Les deux approches offrent donc les mêmes
possibilités et nous avons choisi en S CL de mettre uniquement les descripteurs sur étagère. Si nous
avions choisi l’approche proposée par le modèle Javabeans, il aurait fallu traiter différemment les
archives contenant des descripteurs (instanciation) de celles contenant des composants (clone).
Choix 2 Ce sont les descripteurs de composant qui sont mis sur étagère.
La seconde question est : Assemble-t-on des descripteurs ou des composants ? En effet, l’assem-
blage peut s’adresser aussi bien aux entités conceptuelles (descripteurs) qu’aux entités à l’exécution
3.4. Comment représenter les fonctionnalités des composants ? 77
(composants). D’ailleurs, les principales approches à composants n’offrent pas toutes les mêmes pos-
sibilités sur ce point [Lau et Wang, 2005b; Lau et Wang, 2005a]. Par exemple, en Javabeans et Arch-
Java, seul l’assemblage d’instances de composants est possible. Par contre, en ComponentJ ou en
Scala [Odersky et Zenger, 2005], ce sont les descripteurs de composants qui sont assemblables. En
Scala, les composants — attention il s’agit de descripteurs — sont des classes et l’assemblage de com-
posants correspond essentiellement à un mécanisme de combinaison de classes basé sur des mixins.
Dans ce genre d’approches, l’instanciation d’un descripteur n’est possible que si toutes ses dépen-
dances requises sont satisfaites. Ces modèles sont généralement sûrs mais contraignant puisqu’ils
interdisent d’avoir des composants ayant des dépendances partiellement satisfaites. A l’inverse, en
ArchJava, toutes les dépendances d’un composant peuvent ne pas être nécessaires si seulement une
partie de ses fonctionnalités sont utilisés dans l’application. Nous pensons que l’idéal serait qu’un
langage à composants qui propose des mécanismes d’assemblage aussi bien au niveau des descrip-
teurs que des instances. En effet, cela permettrait de définir les descripteurs mais aussi de construire
une application avec les mêmes mécanismes d’assemblage unifiés. Aucun langage n’intègre cela ac-
tuellement et il semble que cet objectif relève de la problématique de faire un langage à composants
réflexif. En effet, dans un tel langage, les descripteurs seraient des composants à part entière et pour-
raient être assemblés. Dans cette thèse nous avons fait le choix suivant pour S CL :
Avec ce choix nous fixons que ce sont les instances qui sont assemblées. Comme nous l’avons
dit, l’idéal serait de pouvoir assembler aussi bien des descripteurs de composants que des instances.
Ce choix constitue donc une étape nous permettant de définir un langage simple. Il laisse toutefois
la perspective de proposer une version réflexive de S CL où les descripteurs de composants seront
des composants et par conséquent assemblables. De plus, assembler des instances plutôt que des
descripteurs offre de bien meilleures perspectives en terme de dynamicité.
Les fonctionnalités représentent les traitements que peuvent accomplir des composants. Dans
certains modèles, un composant n’a qu’une seule fonctionnalité. Par exemple, les processus Unix
peuvent être considérés comme des composants accomplissant des traitements sur leurs flux d’en-
trée (stdin), de sortie (stdout) et d’erreur (stderr). Deux processus peuvent être combinés en rediri-
geant le flux de sortie du premier vers l’entrée standard du second par exemple. Ce style architectural,
souvent appelé pipes and filters, est représenté sur la figure 3.1.
Toutefois, dans la plupart des modèles actuels, un composant possède plusieurs fonctionnalités
qui sont représentées par des services. Un service est généralement une fonction (ou opération) dé-
finie par un composant, qui possède un nom, des paramètres3 et un résultat. Un service est aussi
3 Le terme « paramètre » (ou « paramètre formel ») permet de désigner une variable liée dans une fermeture lexicale, par
exemple x est un paramètre dans la définition de fonction suivante f(x)..., alors que le terme « argument » (ou « paramètre
réel ») permet de désigner la valeur substituée à un paramètre lors de l’application d’une fonction, par exemple 3 est un
argument dans l’appel de fonction suivant f(3).
78 Chap 3. Spécification de S CL : un langage à composants minimal
stderr
stdin Filter stderr
stdout Pipe stdin Filter
stdout
F IG . 3.1 : Les processus Unix (pipes and filters) vus comme des composants
défini comme un ensemble de fonctions dans certains modèles. Par exemple, un service de gestion
de compte peut se décomposer en trois sous-services : consultation, retrait et dépôt.
L’invocation de service (ce mécanisme est présentée de façon plus complète et détaillée dans la
section 3.9) est le terme utilisé dans cette thèse pour désigner le mécanisme permettant à un com-
posant d’exécuter un service suite à la réception d’une invocation et émettre des invocations de ser-
vices à d’autres composants. Ce terme permet de ne pas le confondre avec l’envoi de message dans
le monde objets. Comme nous le verrons dans la suite, la différence avec l’envoi de message est liée
au mécanisme d’assemblage spécifique au monde des composants. Le définition suivante permet de
fixer le consensus adopté par la majorité des modèles actuels :
Définition 4 (Service fourni) fonction définie dans le code source d’un composant et offerte aux autres
composants pour qu’ils puissent l’invoquer.
Un service fourni peut être assimilé à une méthode publique dans le monde objet. Un compo-
sant peut aussi posséder des services qu’il ne fournit pas afin de factoriser son implémentation par
exemple.
Un composant exprime aussi les services qu’il requiert d’autres composants. Ces services requis
ne sont pas définis par le composant qui peut tout de même les invoquer dans son code.
Définition 6 (Service requis) service nécessaire au fonctionnement d’un composant (invoqué dans le
code de ses services fournis) et fourni par d’autres composants.
Nous avons défini les prises d’un composant comme étant ses points d’accès permettant son
assemblage (cf. définition 2). Dans cette section, nous allons essayer de déterminer la nature de ces
prises et leur rôle exact.
3.5. Que sont les prises des composants ? 79
D’après la section précédente, on peut légitimement supposer que les services peuvent jouer le
rôle de prise des composants. La connexion de composants serait alors réalisée par la liaison d’un
service requis avec un service fourni. Ce mécanisme de connexion basé sur l’appariement de ser-
vices rappelle l’analogie avec le langage C présentée dans la section 3.2. Toutefois, ce mécanisme est
souvent critiqué pour sa granularité trop fine : « [...] component systems should offer the possibility
to connect many required and provided services at once » [McDirmid et al., 2001a]. Nous pensons en
effet qu’il faut proposer un mécanisme de connexion offrant un niveau de granularité plus important
afin de permettre un meilleur passage à l’échelle. Cela suppose donc que les prises des composants
aient un niveau de granularité intermédiaire compris entre celui d’un unique service et celui d’un
composant entier.
Les interfaces de composants sont des prises de granularité intermédiaire : « a component can
only be accessed through well defined interfaces » [Szyperski, 2002]. Attention, il ne faut pas confondre
avec la notion d’« interface » au sens des langages à objets comme Java avec celle d’« interface de
composants » même si ce n’est pas sans rapport comme nous le verrons dans la suite. Les approches à
composants utilisent des concepts différents pour représenter les interfaces de composants comme :
les ports, les interfaces (au des LOO), les interfaces de port [Aldrich et al., 2002b], les protocoles [Plásil
et Visnovsky, 2002], les contrats [Jézéquel et Meyer, 1997] ou encore les réseaux de Pétri [Bastide et
Barboni, 2006]. Par exemple en Fractal, les interfaces de composants — appelées interfaces par abus
de langage [Bruneton et al., 2004] — sont des points d’accès du composants décrits par une interface
Java. A cause de cet abus de langage, les interfaces de composants sont souvent confondues avec
les interfaces Java alors qu’elles remplissent des fonctions différentes comme nous le verrons dans
la suite. En UML, les interfaces de composants sont représentées à l’aide des concepts de port et
d’interface. Un composant UML fournit ou requiert des interfaces à travers ses ports mais il peut
aussi directement (sans passer par un port) implémenter ou requérir une interface. En ArchJava, les
composants ont des ports ; chaque port est décrit par une seule interface qui spécifie les services
fournis et/ou requis à travers ce port. Face à cette diversité, nous devons intégrer une représentation
simple et suffisamment générale des interfaces de composants en S CL.
Tout d’abord, nous pensons qu’il faut distinguer au moins deux problématiques dans lesquelles
sont utilisées les interfaces de composants :
– l’assemblage des composants où les interfaces de composants sont vues comme les points de
connexion des composants et sont le support des invocations de services. Le concept de port
est généralement utilisé pour représenter cette facette des interfaces de composants.
– la description des composants où les interfaces de composants sont vues comme des descrip-
tions (syntaxiques, comportementales, de qualités) permettant par exemple la vérification des
assemblages ou la validation des utilisations du composant. Les notions d’interface (au sens
des LOO), de protocole ou de contrat sont par exemple utilisées pour représenter cette facette
des interfaces de composants.
La suite de cette section présente ces deux problématiques séparément en se concentrant dans
un premier temps sur le concept de port et ses variations puis sur le concept d’interface au sens large
(interface des LOO, protocole, contrat). Nous détaillons ensuite un exemple en S CL afin d’illustrer les
choix que nous avons fait en ce qui concerne les interfaces de composant.
80 Chap 3. Spécification de S CL : un langage à composants minimal
Les ports remplissent une double fonction pour le composant, ils sont le support de ses
connexions et de ses communications via l’invocation de ses services fournis et requis. L’étude des ap-
proches existantes nous a permis de distinguer les ports unidirectionnels et les ports bidirectionnels.
Les ports unidirectionnels regroupent un ensemble de service fournis ou requis comme en Compo-
nentJ ou Fractal. On parle alors de port fourni et de port requis. A travers un port bidirectionnel des
services peuvent être requis et fournis comme c’est le cas en ArchJava ou en UML.
Dans les deux cas, un port définit un point de vue et une politique de sécurité. Les ports requis
définissent les points de vue que le composant peut avoir sur des composants externes tandis que les
ports fournis définissent les points de vue que les composants externes pourront avoir sur ce compo-
sant. De même, lorsqu’un composant est accédé via l’un de ses ports, ce dernier est le garant d’une
politique de sécurité, c’est-à-dire que les composants clients ne pourront utiliser que les services dis-
ponibles via ce port. En permettant de panacher des services requis et des fournis au sein d’un même
port, les ports bidirectionnels permettent de décrire plus précisément les dépendances entre les ser-
vices. Ils peuvent être considérés comme le point de vue du composant sur une collaboration (au
sens d’UML).
Bien que cela puisse sembler restrictif au premier abord, nous avons choisi d’intégrer, des
ports unidirectionnels en S CL. Ce choix 4 est essentiellement motivé par notre objectif 3 de non-
anticipation. En effet, utiliser deux ports, l’un requis et l’autre fourni à la place d’un seul port bidi-
rectionnel lors de la conception d’un composant permet ensuite de laisser l’architecte décider si le
composant qui va utiliser les services fournis est le même que celui qui va fournir les services re-
quis. Imposer que ce soit le même composant client lors de la conception est restrictif et relève de
la description de contraintes d’assemblage décrites dans la section 3.5.2. Notre idée est de fournir
des composants sur étagère simples et peu contraints de sorte qu’ils puissent être adaptés et réutili-
sés dans différents contextes. Les ports uniderectionnels sont aussi plus simples à comprendre et à
utiliser en pratique. De plus, la perspective de construire (si nécessaire) des ports bidirectionnels en
agglomérant des ports unidirectionnels reste envisageable.
Suite à ce choix 4, la définition 7 pose le vocabulaire que nous utilisons dans la suite.
Définition 7 (Port requis (resp. Port fourni)) point d’assemblage d’un composant possèdant un nom
et à travers lequel est requis (resp. fourni) un ensemble de services.
Il est à noter qu’en S CL, deux ports d’un même composant ne peuvent pas posséder le même nom
mais peuvent exporter le même service fourni (s’il s’agit de ports fournis) ou requérir des services de
signatures identiques (s’il s’agit de ports requis).
Notation graphique. La figure 3.2 présente les conventions graphiques utilisées dans la suite. Nous
n’avons pas utilisé directement la notation UML car elle trop dépendante du modèle de composants
3.5. Que sont les prises des composants ? 81
sous-jacent. Par exemple, en UML ce sont les interfaces qui sont fournies ou requises et non les ports.
La notation que nous proposons s’inspire toutefois largement de la notation UML. Un composant est
un rectangle. Les ports sont représentés sur la périphérie des composants. Un demi-cercle convexe
représente un port fourni alors qu’un demi-cercle concave représente un port requis.
Un composant
Un port
Un port requis
fourni
De façon générale, les interfaces sont un support de description des composants permettant de
spécifier comment ils peuvent être assemblés ou utilisés au sein d’une architecture. Les interfaces
se situent soit à un niveau local (associées à un port) soit à un niveau global (associées à un compo-
sant). Les interfaces définissent des contrats généralement classés en quatre niveaux [Beugnard et al.,
1999] :
Syntaxique : ces contrats spécifient les signatures des services fournis et requis (nom, paramètres,
résultat, exceptions).
Comportementaux : ces contrats spécifient comment peut être utilisé un composant.
De synchronisation : ces contrats sont nécessaires dans un contexte distribué et/ou concurrentiel
afin de spécifier le comportement des composants en terme de synchronisations entre les in-
vocations de services.
De qualité de service : ces contrats sont généralement cruciaux dans le domaine de l’informatique
embarquée ou temps réel afin d’assurer des contraintes de qualité au sein d’une architecture
globale.
Les approches à composants proposent des notations formelles ou informelles adaptées à leurs
besoins pour décrire des contrats de ces différents niveaux. Les contrats syntaxiques sont les plus
simples et peuvent être définis à l’aide de langages de définition d’interface (Interface Definition
Language – IDL) ou directement avec la notion d’interface des LOO (comme les interfaces Java).
D’ailleurs, les principaux langages de programmation à composants se limitent généralement à ce
niveau de contrat via l’utilisation d’interfaces comme en Fractal ou ComponentJ. En ArchJava, une
interface spécifie aussi pour chaque service s’il est requis ou fourni puisque chaque port est bidirec-
tionnel et est décrit par une seule interface. SOFA supporte des contrats comportementaux puisqu’il
permet de décrire les enchaînements d’invocations de services valides – notion de protocole – avec
un formalisme à base d’expressions régulières. Un tel formalisme a aussi été utilisé pour décrire les
82 Chap 3. Spécification de S CL : un langage à composants minimal
protocoles de services web [Tremblay et Chae, 2005]. Prenons l’exemple d’un composant dédié à des
communications réseaux fournissant les trois services suivants : open(adr) (ouvre une connexion
réseau à l’adresse spécifiée par le paramètre adr), send(data) (envoie les données data à travers la
connexion) et close (ferme la connexion). Un protocole pour ce composant permet de décrire que
l’ordre d’invocation de ces trois services pour une utilisation valide est : open (invoqué une seule
fois), puis send (autant de fois que nécessaire) et finalement close (une seule fois). D’autres for-
malismes peuvent être utilisés pour exprimer les contrats comportementaux comme les machines à
états d’UML, les langages à automates [de Alfaro et Henzinger, 2001] ou encore les protocoles symbo-
liques [Pavel et al., 2005]. Les contrats de synchronisation ou de qualité de service sont peu caractéri-
sés dans les langages à composants existants et les solutions apportées à ces problématiques restent
confinées dans des ADL dédiés à la spécification et ne permettant pas de produire directement une
application exécutable.
Le domaine de la vérification des architectures logicielles à base de composants vise à valider les
assemblages et les utilisations des composants en se basant sur le respect des contrats établis par
les interfaces des composants. Vérifier la conformité de contrats comportementaux dépend du for-
malisme choisi. Par exemple, si les comportements des composants sont décrits avec des réseaux de
Pétri à objets (RdPO) [Passama, 2006], vérifier la validité d’une architecture revient à construire et
valider le RdPO de l’architecture globale à partir des RdPO des composants et de leurs connexions.
Dans le cas du système ConFract [Collet et al., 2005], un contrat a une portée (interface, composant,
ensemble d’interfaces, etc.) et il est créé à partir de spécifications exécutables écrites en CCL-J (Com-
ponent Constraint Language for Java. Le respect des contrats est ensuite automatiquement vérifié
pendant l’exécution.
Du point de vue syntaxique, on constate que la conformité des interfaces entre elles est sou-
vent établie par la compatibilité des types qui leur ont été associés. Lorsque deux composants
sont connectés via leurs ports, cela suppose que les types de leurs interfaces sont compatibles. Par
exemple, un port requis typé par une interface I 1 et connecté à un port fourni typé par une interface
I 2 suppose que le type défini par I 1 est un super-type de celui défini par I 2 . On distingue générale-
ment deux sortes de systèmes de types : ceux basés sur des noms (named type systems) comme en
Java et ceux basés sur la structure (structural type systems) comme en Objective CAML [Loulergue,
2004]. L’utilisation de noms permet de capturer la sémantique [Büchi et Weck, 1998] :
« [...] types stand for semantical specification. While the conformance of an implemen-
tation to a behavioral specification cannot be easily checked by current compilers, type
conformance is checkable. By simply comparing names, compilers can check that several
parties refer to the same standard specification. »
Les systèmes de types structurels [Cardelli, 1997] sont moins expressifs mais offrent un meilleur
découplage entre les entités puisque la relation de sous-typage est déduite de la structure des inter-
faces et non d’un nom commun. Par exemple, spécifier qu’un « composant requiert une pile » est plus
sémantique que « un composant requiert deux services push et pop ». Toutefois, dans le premier cas,
il doit exister une interface pile (de façon globale) et le composant ne pourra être connecté qu’à un
autre composant fournissant cette même interface pile ou l’un de ses sous-types. Dans le deuxième
cas, une interface peut être un sous-type d’une autre sans qu’elles aient de relation directe puisque la
relation de sous-typage est déduite de la structure.
3.5. Que sont les prises des composants ? 83
En S CL, nous avons choisi d’intégrer un modèle simple en ce qui concerne la description des
composants. Ainsi, une interface est locale et attachée à un port.
Actuellement, nous n’avons pas intégré à S CL d’interfaces globales à un composant. Cela serait
certainement nécessaire pour définir des contrats globaux afin de faire de la vérification d’architec-
tures ou décrire des contraintes portants sur plusieurs ports.
Toutefois, nous nous limitons pour l’instant à des vérifications du niveau syntaxique pour les-
quelles des interfaces de ports sont suffisantes.
Choix 6 Une interface spécifie un ensemble de signatures des services. La compatibilité d’interface re-
pose sur la relation de sous-typage entre leurs types qui est basée sur l’inclusion des ensembles de signa-
ture de services.
Une interface n’a pas besoin d’un nom en S CL et la relation de sous-typage est structurelle. Cette
décision est motivé par notre volonté de découplage des composants. En effet avec un système de
types basé sur les noms, deux composants ne peuvent être connectés que si les types de leurs inter-
faces sont en relation directe. Une approche structurelle semble offrir une meilleure indépendance
des composants lors de leur définition au détriment de la sémantique comme nous l’avons vu. Cette
vision est parfois appelée « Duck typing » (typage du canard) [Anantharam, 2001] :
« This method . . . [of] . . . just relying on what methods it supports is known as “Duck Ty-
ping”, as in “if it walks like a duck and quacks like a duck. . . ”. The benefit of this is that
it doesn’t unnecessarily restrict the types of variables that are supported. If someone comes
up with a new kind of list class, as long as it implements the join method with the same
semantics as other lists, everything will work as planned. »
3.5.3 Exemple en S CL
En résumé, une interface de composant est représentée en S CL par un port unidirectionnel dé-
crit par une interface. Un exemple de composant S CL est montré à la figure 3.3. Le composant pm est
une instance du descripteur PASSWORD M ANAGER. Chacun de ses ports est décrit par une interface qui
est représentée sur le schéma par un rectangle en pointillé contenant les signatures de services dé-
crites par cette interface. Ce composant permet d’une part de générer des mots de passe composés de
lettres et de chiffres via son service generatePwd ou composés de chiffres uniquement via son service
generateADigitsOnlyPwd et d’autre part de vérifier qu’un mot de passe n’est pas trop simple (taille
suffisante, mot inexistant dans un dictionnaire, etc.) via son service isValid. Pour fournir ces trois
services, un composant PASSWORD M ANAGER a besoin d’un service de génération de nombre aléatoires
nommé getRandomNumber qu’il requiert à travers son port Randomizer.
84 Chap 3. Spécification de S CL : un langage à composants minimal
pm : PasswordManager
Generator
generatePwd(size)
generateADigitsOnlyPwd(size) Randomizer
isValid(Pwd) getRandomNumber()
Checker
descriptor PasswordManager {
providedport Generator, IGenerator
providedport Checker, IChecker
requiredport Randomizer, IRandomizer
pm := new PasswordManager
4 Pour la présentation de S CL , nous n’utilisons pas la syntaxe du prototype actuel (cf. chapitre 5) car elle est très proche
de celle du langage Smalltalk qui est bien souvent mal connue. Nous avons donc opté pour un pseudo-langage (dérivé de
Ruby) ayant une syntaxe plus proche des langages à objets actuels (Java, C#) tout en étant en typage dynamique.
3.6. Les objets primitifs sont-ils des composants ? 85
Un objet primitif est nativement manipulé par l’architecture cible. Les types de ces objets sont
entre autres : les petits entiers, les booléens, les caractères et les flottants.
Dans les langages à objets comme Smalltalk, les objets primitifs sont représentés de façon uni-
forme au niveau du modèle mais bénéficient tout de même d’une implémentation spécifique pour
des raisons d’efficacité. Il existe plusieurs techniques (e.g mise en boîte appelée boxing) permettant
de simuler qu’un objet primitif est instance d’une classe tout en optimisant ses traitements par rap-
port à une instance réelle. Tous les langages à objets ne traitent pas uniformément les objets primitifs
et les objets comme c’est le cas en C++ par exemple. Java distingue aussi les types primitifs (int, float,
boolean, etc.) de ceux définis par des classes. Toutefois, depuis sa version 1.5, Java supporte supporte
l’auto-boxing permettant la conversion automatique d’un objet primitif en une instance de classe et
inversement. Considérer les objets primitifs comme des objets permet de proposer au programmeur
une vision unifiée. Il manipule ainsi les objets primitifs comme des objets standards pouvant recevoir
des messages et profiter du polymorphisme.
La question que l’on se pose dans cette section est la suivante : Peut-on considérer les objets
primitifs comme des composants dans un langage à composants ? La plupart des modèles existants
n’abordent pas cette problématique sauf Fractal qui considère que les objets Java multi-interfacés
sont des composants de base ce qui permet de mettre fin à la définition récursive des compo-
sants [Bruneton et al., 2002]. Par contre ArchJava distingue clairement les objets Java (instance d’une
classe) et les composants (instance d’une classe de composant). Cette cohabitation entre les objets
et les composants en ArchJava est problématique pour le programmeur. Par exemple, seul un ob-
jet peut être passé en argument lors d’un envoi de message. Afin de proposer une vision unifiée au
programmeur en S CL, nous avons fait le choix suivant :
Choix 7 Un objet primitif doit être considéré comme un composant possédant un unique port fourni
nommé defaut à travers lequel il fournit un ensemble de services.
Ce choix 7 permet d’intégrer les objets primitifs comme des composants comme l’illustre la fi-
gure 3.4. De façon analogue à Smalltalk où un entier est présenté au programmeur comme une ins-
tance de classe SmallInteger alors qu’il est traité d’une façon spécifique par la machine virtuelle, un
entier est présenté au programmeur S CL comme un composant.
28 : Integer
Default
<(anInt)
>(anInt)
isOdd()
isEven()
...
Cette solution en S CL permet d’unifier la vision des objets primitifs et des composants au niveau
du modèle. Bien évidemment un interprète de S CL devra traiter ces objets primitifs de façon spéci-
fique (cf. chapitre 5).
Dans le cas d’une implémentation avec un langage à objets, cette solution permet même de consi-
dérer un objet comme un composant n’ayant qu’un seul port nommé default à travers lequel toutes
les méthodes publiques (y compris celles héritées) de l’objet sont fournies. Afin d’unifier complète-
ment objets et composants, nous avons fait le choix suivant en S CL :
Choix 8 Tout composant possède un port nommé default à travers lequel tous les services fournis
par le composant sont accessibles. L’instanciation d’un descripteur de composant (via la primitive new)
retourne une référence sur le port default du composant nouvellement créé.
Ce choix 8 nous permet d’unifier les objets et les composants car tous les composants possèdent
un port nommé default. Nous imposons aussi que le mécanisme d’instanciation retourne une réfé-
rence vers le port default du composant instancié au lieu d’une référence vers le composant direc-
tement. Empêcher la détention de référence directe sur un composant permet d’assurer que toutes
les communications s’effectuent via un port et contribue à assurer l’intégrité des communications
comme nous le verrons dans la section 3.9.
Le mécanisme de connexion est central dans l’approche composants (cf. chapitre 2) et se présente
sous diverses formes dans les propositions existantes. En toute généralité, un composant a des prises
et connecter des composants revient à brancher leurs prises. En S CL, les ports sont les prises des
composants et la connexion de composants repose donc sur la liaison de ports5 . Dans la suite, deux
composants sont dits connectés s’il existe une liaison entre leurs ports.
On distingue les mécanismes de liaison de ports n-aires et binaires. Par exemple, ArchJava pro-
pose la primitive n-aire connect permettant de lier directement un ensemble de ports, alors que Frac-
tal propose la primitive binaire bindFc qui lie un port requis à un port fourni. Bien que ces approches
intègrent respectivement des ports bidirectionnels et unidirectionnels, elles soulèvent la question
suivante : Le mécanisme de liaison de ports doit-il être n-aire ou binaire ?
En S CL, nous avons choisi d’intégrer un mécanisme de liaison de port binaire car il est moins su-
jet aux ambiguïtés comme l’est celui de ArchJava quand il existe plusieurs services fournis candidats
pour un même service requis. De plus, les liaisons décrites par le mécanisme n-aire peuvent se dé-
composer en liaisons binaires dans la plupart des cas. Les seuls cas où cela n’est pas possible, c’est
lorsqu’on veut associer des services requis à travers un même port requis à des services fournis par
différents ports. Ces cas peuvent être aisément traités avec des composants adapteurs [Gamma et al.,
1995] ou les connecteurs de S CL que nous présentons dans la section 3.10. Par ailleurs, dans le cas de
5 Nous avons choisi le terme liaison et non pas connexion que nous réservons aux composants afin d’éviter toute am-
biguïté.
3.8. Les liaisons multiples 87
liaisons binaires, la validité d’une liaison entre ports est plus facilement vérifiée que dans le cas n-aire
puisqu’elle repose uniquement sur la compatibilité des deux interfaces qui leur sont associées.
Choix 9 Un port requis peut être lié à un unique port fourni si leurs interfaces sont compatibles.
La liaison de ports permet d’établir que les invocations de services requis à travers un port abou-
tiront à l’exécution des services fournis via un autre port. Un port requis ne peut donc être lié qu’à
un unique port fourni. Par contre, un port fourni peut être lié à plusieurs ports requis. Actuellement,
les contrats définis par les interfaces des ports sont uniquement syntaxiques et la compatibilité entre
interfaces repose sur l’inclusion ensembliste comme l’indique le choix 6. La figure 3.5 montre une liai-
son entre le port requis Randomizer d’un composant pm et le port fourni Generator d’un composant
rng. La représentation graphique d’une liaison de port est une flèche à tirets directement empruntée
à la notation UML. Le sens de la flèche indique le sens des invocations de services.
Checker
getRandomNumber() getRandomNumber()
Le code source 3.2 explicite comment lier des ports de composants. La construction syntaxique
bind port_requis to port_fourni permet de mettre en place une liaison de ports. Cette structure sera
enrichie dans les sections suivantes.
pm := new PasswordManager
rng := new RandomNumberGenerator
Dans cette section, nous traitons le problème des relations multiples entre les composants. Par
exemple, la relation entre un composant Bank et des composants Client. Une telle relation est « mul-
tiple » dans le sens où une banque peut posséder de nombreux clients.
Dans les approches existantes décrites dans le chapitre 2, deux approches existent pour traiter ces
relations multiples :
88 Chap 3. Spécification de S CL : un langage à composants minimal
– SOFA (cf. section 2.2.7) et Fractal (cf. section 2.2.6) permettent de fixer une cardinalité multiple
à une interface de composant ce qui ce traduit dans le code du composant par un tableau ou
une liste en tant qu’attribut de la classe implémentant le composant ;
– ArchJava (cf. section 2.2.8) permet l’ajout dynamique de ports grâce aux notions de port inter-
face et de connect pattern. Ainsi, pour chaque nouveau composant Client, le composant Bank
est automatiquement doté d’un port permettant de le lier à ce nouveau composant.
La solution que nous proposons en S CL est intermédiaire.
Définition 8 (Collection de ports) Collection ordonnée et nommée de ports requis ou de ports fournis.
Chaque port de la collection peut être accédé par un index.
Par ce choix 10 et la définition 8 nous posons qu’un composant S CL peut posséder des collection
de ports. Les collections de ports sont déclarées par le programmeur S CL au même titre que les ports.
Toutefois, la taille de ces collections n’est pas fixée par le programmeur — contrairement à SOFA—
et un interprète S CL peut décider par exemple d’adopter une politique d’allocation de ports à la de-
mande comme en ArchJava. Dans la suite on utilise le terme port multiple pour désigner une collec-
tion de ports. Ceci est un abus de langage car une collection de ports n’est pas un port. Toutefois, ce
terme est intuitif et analogue aux « prises multiples » électriques.
Notation graphique. La figure 3.6 présente la convention graphique pour les ensembles de ports re-
quis (resp. fournis) qui reprend la graphie d’un port requis (resp. fourni) en ajoutant une sur-épaisseur
afin d’illustrer la multiplicité.
Un composant
Dans cette section nous présentons les choix qui nous conduits à définir qu’en S CL, une invoca-
tion de service référence :
3.9. Les bases de l’invocation de services 89
Les invocations de services sont effectuées dans le code source des services des composants. Syn-
taxiquement, une invocation de service est très similaire à un envoi de message si ce n’est qu’elle
s’effectue à travers un port appelé port d’émission.
Choix 11 Une invocation de service s’effectue toujours via un port d’un composant.
Ce choix 11 est motivé par la nécessité d’assurer l’intégrité des communications dans les architec-
tures logicielles [Luckham et al., 1995] c’est-à-dire que toute communication entre composants doit
passer par une liaison décrite dans l’architecture. Comme les liaisons sont établies entre les ports,
imposer que les invocations de services s’effectuent aussi via les ports rend explicite toutes les dé-
pendances qu’elles induisent. En effet, s’il était possible d’invoquer directement un service d’un com-
posant à partir d’un autre, cela introduirait une « dépendance cachées dans le code » entre ces deux
composants qui ne serait pas décrite au niveau de l’architecture. De plus, pour les invocations effec-
tuées via un port requis, comme illustré sur la figure 3.7, le composant destinataire est inconnu de pm
lors de l’implémentation et donc impossible à référencer. C’est lors de l’assemblage que le compo-
sant destinataire sera choisi et fixé via le mécanisme de liaison de ports. Les invocations de services à
travers un port requis provoqueront alors l’exécution des services de ce composant destinataire.
pm : PasswordManager
Generator generatePwd(size) {
generatePwd(size) ...
generateADigitsOnlyPwd(size) i := Randomizer.getRandomNumber() Randomizer
...
}
generateADigitsOnlyPwd(size) { ... }
Les invocations via les ports requis permettent un meilleur découplage puisque l’établissement
ou le retrait des liaisons de ports permet de fixer le composant qui traitera effectivement les invoca-
tions de services faites à travers ces ports. Dans ce contexte, les invocations de services à travers des
ports fournis sont-elles nécessaires ? Contrairement aux invocations via des ports requis, elles ne favo-
risent pas le découplage puisque le port d’émission référencé dans le code est un port fourni et de ce
90 Chap 3. Spécification de S CL : un langage à composants minimal
fait il appartient au composant destinataire qui traitera effectivement l’invocation. Bien qu’elles ne
favorisent pas le découplage et fixent le composant destinataire, les invocations de services à travers
des ports fournis nous ont semblé nécessaires pour deux raisons : invoquer des services internes et
invoquer des services fournis par des sous-composants dans les composites comme nous le verrons
dans la section 3.11.
Puisque les invocations de services s’effectuent toujours via un port et que les services internes
ne sont pas fournis, comment est-il possible d’invoquer des services internes ? Ce problème est gé-
néralement pas ou peu abordé dans les langages à composants existants. En Julia et en ArchJava,
les composants sont implémentés par une classe Java et il est donc possible d’invoquer un service
interne via l’envoi de message en utilisant la pseudo-variable this. En S CL, nous posons les choix
suivants :
Choix 12 Tout composant possède un port interne nommé self via lequel tous les services définis dans
le composant sont fournis.
Définition 9 (Port interne) port (requis ou fourni) d’un composant qui n’est pas accessible en dehors
de son implémentation.
Les choix 12 et la définition 9 permettent de proposer une solution intégrée et uniforme pour
l’invocation des services internes. Un port interne n’est utilisable que dans l’implémentation de son
composant au même titre que les interfaces internes en Fractal. Un tel port peut aussi être décrit
par une interface, supporter des liaisons de ports et des invocations de services. Toutefois, il est in-
visible et inaccessible pour les autres composants. Tout composant est muni d’un port interne self
et c’est pourquoi nous l’omettons souvent dans nos représentations graphiques. La figure 3.8 montre
un exemple de représentation de ce port interne.
pm : PasswordManager
Generator Self
generatePwd(size) {
... Randomizer
newPwd := ""
i := Randomizer.getRandomNumber()
...
if( self.isValid(newPwd) )
return newPwd
else
return self.generatePwd(size)
}
Checker
...
Dans la suite, les ports accessibles de l’extérieur d’un composant sont appelés ports externes pa-
reillement aux interfaces externes de Fractal. L’utilisation que nous proposons pour les ports internes
requis est détaillée dans la section 3.11.
3.9. Les bases de l’invocation de services 91
Les paramètres de services et le passage des arguments lors de l’invocation de service soulèvent
de nombreuses questions : Qu’est-ce qu’un paramètre ? A-t-on réellement besoin de paramètres sa-
chant qu’il est possible d’utiliser la connexion de composants ? Si oui, qu’est-ce qu’un passage d’ar-
guments ? Nous allons montrer que ces questions ne sont pas vraiment traitées dans les langages
existants bien qu’elles constituent un enjeu majeur pour l’intégrité des communications et l’unifor-
mité du langage.
Dans les approches existantes, les composants peuvent s’échanger des données diverses via le
passage d’arguments et le retour de résultats lors des invocations de services. Une approche consiste
à standardiser le format des données échangeables comme c’est le cas dans le modèle des processus
Unix (Pipe and Filter) où les données échangées sont toujours des flux de caractères ou encore dans
le modèle des Services Web avec le format SOAP spécifiant comment sont structurés les requêtes de
services, les données et les réponses. Les langages tels que ArchJava, ComponentJ ou encore Julia re-
posent entièrement sur le passage d’arguments du langage Java. En Julia, un composant Fractal est
implémenté par un ensemble d’objets Java chacun représentant une partie du composant (interface,
membrane, contenu) et ayant des responsabilités différentes. Le programmeur a accès à ces différents
objets, il peut donc passer ces objets comme arguments lors des envois de messages. Cette possibilité
conduit à « la violation de l’intégrité des communications » [Léger et al., 2006] c’est-à-dire que des
objets Java représentants deux composants différents peuvent communiquer directement (avec un
échange préalable de références) sans passer par les interfaces et les liaisons de leurs composants.
Face à ce problème, la solution développée dans [Léger et al., 2006] est un contrôle dynamique des
communications entre objets afin de vérifier leur validité par rapport à l’architecture. Nous pensons
qu’il est possible de s’affranchir de ce contrôle dynamique en spécifiant clairement dans le langage
S CL ce que les composants peuvent échanger et comment ils peuvent le faire dans le respect de l’in-
tégrité des communications. Cela implique que le programmeur S CL ne devra pas avoir accès aux
objets sous-jacents à un composants dans le cas d’une implémentation dans un LOO.
Actuellement, aucune opération n’est possible directement sur un composant. Les invocations de
service et les connexions s’effectuent via les ports et c’est pourquoi nous avons fait le choix suivant
en S CL :
Choix 13 Les paramètres des invocations de services sont des références sur des ports.
Par ce choix 13, nous répondons à notre question initiale en fixant la nature des paramètres. Les
paramètres étant des références sur des ports, quelle est la différence entre un paramètre et un port
requis ? Pour illustrer cette différence, la figure 3.9 présente deux conceptions différentes du descrip-
teur PASSWORD M ANAGER. Dans le cas (a), le service generatePwd prend un paramètre size alors que
dans le cas (b), le descripteur pm2 possède un port Configurator à travers lequel il requiert un ser-
vice getSize qu’il invoque dans l’implémentation du service generatePwd.
La différence entre les paramètres des services et les ports requis est, hormis la syntaxe, la portée
de l’identificateur (scope) et sa durée de vie (extent). C’est la même différence qu’entre un paramètre
et un attribut dans un LOO. La question suivante se pose alors : faut-il garder les paramètres des
services en S CL alors qu’il serait possible de n’utiliser que des ports requis ? Utiliser systématiquement
92 Chap 3. Spécification de S CL : un langage à composants minimal
(a) (b)
Generator Generator
generatePwd(size) { generatePwd() { Randomizer
... ...
} size := Configurator.getSize()
...
Randomizer } Configurator
des ports requis plutôt que des paramètres permet d’assurer l’intégrité des communications puisque
le mécanisme d’assemblage est utilisé. En effet, s’il est possible de passer des références sur des ports
fournis en tant qu’arguments, l’intégrité des communications pourrait être violée de la même façon
qu’en Julia c’est-à-dire en stockant cette référence pour l’utiliser ultérieurement. Toutefois, il n’est pas
envisageable de n’utiliser que des ports requis du fait des différences en les paramètres des services
et les ports requis. La solution intégrée en S CL est la suivante :
Nous avons donc choisi de décrire le mécanisme de passage d’arguments de S CL en terme de liai-
sons de ports. Par ce choix, nous proposons pour S CL un mécanisme uniforme et respectueux de l’in-
tégrité des communications puisqu’il est impossible que les composants communiquent en dehors
d’une connexion. La figure 3.10 présente un exemple d’invocation de service utilisant ce mécanisme
dont les étapes de traitement sont les suivantes :
1. Emission d’une invocation de service via un port. Dans la figure 3.10, le composant am émet, via
son port PwdGenerator, une invocation de service ayant pour sélecteur generatePwd et pour
seul argument, une référence sur l’unique port du composant primitif représentant la valeur 6
(cf. choix 7).
2. Le composant destinataire (pg dans notre exemple) reçoit et traite l’invocation. Nous revien-
drons sur la façon dont un composant traite une invocation de service dans la section 3.12.
3. Les ports passés en argument de l’invocation sont liés aux ports args du composant destina-
taire (cf. figure 3.10).
4. Le service est exécuté. Un mécanisme d’aliasing transparent (mis en place par l’évaluateur
par exemple) permet au programmeur d’utiliser les noms qu’il a spécifié pour les paramètres
3.10. Les connexions doivent-elles être réifiées ? 93
dans l’implémentation et ne lui impose pas d’utiliser directement les ports args. Dans notre
exemple, le code source du service generatePwd du composant pg utilise l’identificateur size
comme nom de paramètre et non args[1].
5. A la fin de l’exécution du service, toutes les liaisons des ports args sont supprimées.
PwdGenerator Generator
Manager
newAccount(pseudo) {
newAccount(pseudo) ...
deleteAccount(pseudo) newPwd := PwdGenerator.generatePwd(6)
... generatePwd(size)
}
findAccount(pseudo) Args
... generatePwd(size)
Consultor generateADigitsOnlyPwd(size)
Args
Etape 3-4
am : AccountManager pg : PasswordGenerator
PwdGenerator
Manager generatePwd(size) { Args
# alias size to args[1]
...
}
Generator
Consultor 6 : Integer
Args
Default
Les connexions entre les composants sont réalisées par des liaisons de ports. Bien que le méca-
nisme de liaison de ports soit utilisé dans la majorité des modèles actuels, il présente au moins les
deux inconvénients suivants : la difficulté à résoudre les incompatibilités et le passage à l’échelle.
Une incompatibilité (mismatch en anglais) [Garlan et al., 1995] se produit lorsque la connexion
de deux composants a du sens mais qu’elle est impossible à établir à cause de problèmes syntaxiques
ou structurels. Ce problème est souvent évoqué sous des noms différents comme la compatibilité des
composants du point de vue des branchements (plug-compatible components) [Achermann et al.,
2001] en Piccola ou encore les conflits d’interfaces (interface conflicts) [Fröhlich, 2000] en Lagoona.
Ces incompatibilités sont inévitables [Sametinger, 1997] puisque elles sont la conséquence de la non-
anticipation des connexions. En effet, un composant n’est pas développé pour être connecté ou pour
94 Chap 3. Spécification de S CL : un langage à composants minimal
coopérer avec un composant donné (un type de composants en particulier) mais avec d’autres com-
posants fournissant les services qu’il requiert. Ces incompatibilités peuvent être levées par l’applica-
tion du schéma de conception adapteur [Gamma et al., 1995] c’est-à-dire la création d’un composant
spécifique permettant de faire collaborer des composants qui ne le pourraient pas directement. La
réification des connexions sous la forme de connecteurs [Shaw, 1996] permet de résoudre plus facile-
ment ces incompatibilités structurelles. Les connecteurs permettent aussi :
– une meilleure séparation entre le code relatif à l’interaction entre les composants et le code mé-
tier. Typiquement, le code relatif aux communications distantes est à inclure dans les connec-
teurs.
– d’associer la sémantique des connexions aux connecteurs et éventuellement de le rendre adap-
table au cas par cas.
– réutiliser directement des protocoles de communication complexes.
– capitaliser sur les connecteurs c’est-à-dire de proposer des bibliothèques de connecteurs réuti-
lisables chacun proposant un protocole de communication particulier.
Tous ces avantages nous conduisent à réifier les connexions en S CL.
Choix 15 Les connecteurs sont des entités de première classe (manipulables dans le langage).
Suite à ce choix 15, la question suivante se pose : qu’est-ce qu’un connecteur ? Il existe deux ap-
proches : celles où les connecteurs ne sont pas des composants comme en ArchJava et en SOFA et
celles où les connecteurs sont des composants comme en Fractal. En ArchJava [Aldrich et al., 2003],
les composants sont décrits par des classes de composants (décrivant les ports) alors que les connec-
teurs sont décrits par des classes standards utilisant l’API java.lang.reflect afin de manipuler et
d’adapter si besoin les envois de message échangés par les composants via leurs liaisons de ports. De
même, en SOFA les connecteurs ne sont pas des composants bien qu’ils peuvent être construits à par-
tir de composants comme nous l’avons vu dans la section 2.2.7. En Fractal par contre, un connecteur
est un composant (binding component) standard jouant le rôle d’adapteur pour d’autres composants.
En S CL, nous avons choisi cette dernière approche.
Ce choix 16 est essentiellement motivé par notre volonté d’uniformité et de simplicité. A ce stade,
S CL propose une solution identique à celle de Fractal en ce qui concerne les connecteurs qui sont des
composants standards jouant un rôle d’adapteur.
Cette solution n’est toutefois pas suffisante comme nous allons le voir à travers l’exemple de la
figure 3.11. Dans cet exemple, les composants pm et rng ne peuvent être connectés directement car le
premier requiert un service nommé getRandomNumer alors que le second fourni un service nommé
random. L’utilisation du composant adapteur pm2rng permet de résoudre cette incompatibilité. Bien
que cette solution fonctionne, nous pensons qu’elle pose plusieurs problèmes. Tout d’abord, le com-
posant pm2rng doit obligatoirement fournir un service nommé getRandomNumer afin d’être lié au
composant pm. Ce nom de service imposé est en rapport ni avec la sémantique du code source, ni avec
celle du composant adapteur. En effet, cet adapteur ne fournit pas un service générant des nombres
3.10. Les connexions doivent-elles être réifiées ? 95
aléatoires mais adapte le service requis random afin qu’il puisse être utilisé par le composant pm.
Cette adaptation est nécessaire car le service requis getRandomNumber, utilisé pour générer des mots
de passe, doit retourner des nombres aléatoires compris entre 1 et 26 (pour le choix d’une lettre de
l’alphabet) alors que le service random retourne des flottants entre 0 et 1. Par ailleurs, la question qui
se pose est de savoir comment, avec cette solution, il serait possible de définir des adapteurs géné-
riques. Dans cet exemple, le composant adapteur pm2rng a été construit de façon ad hoc afin de faire
collaborer les deux composants pm et rng et il ne pourrait certainement pas être réutilisé pour adapter
d’autres composants. Enfin, cette solution est mal adaptée pour le passage à l’échelle puisque l’écri-
ture d’un adapteur peut s’avérer très fastidieuse dans le cas d’une connexion de plusieurs composants
ayant de nombreux services.
Face à toutes ces limitations, une solution consiste à utiliser une approche par génération de code.
La génération de code est par exemple utilisée dans l’environnement Javabeans pour générer au-
tomatiquement des adapteurs Dans les cas d’adaptation triviaux, le code généré est complètement
masqué à l’architecte. Dans les autres cas (comme notre exemple de la figure 3.11), le code généré
n’est qu’un squelette que l’architecte doit compléter. Nous n’avons pas retenu la génération de code
pour S CL car elle contredit nos objectifs initiaux de simplicité et de fournir des abstractions et mé-
canismes de haut niveau. En effet, le code généré peut être complexe, difficile à maintenir et à faire
évoluer au fil des évolutions.
Nous proposons une solution novatrice à ce problème en S CL. Cette solution est fondée sur les
constats suivants :
– un composant adapteur ne peut pas être utilisé directement comme connecteur à cause de la
conception ad hoc que cela implique comme nous l’avons montré ci-dessus (cf. figure 3.11) ;
– du point de vue de la réutilisation et de la dynamicité, les composants adapteurs ne sont pas
très adaptés car ils doivent être conçus spécifiquement c’est-à-dire dotés de ports et de services
spécifiques en fonction des composants à connecter ;
– le code des connecteurs relève, du point de vue des communications entre les composants, du
méta-niveau puisqu’il manipule les invocations de services et les résultats afin de les adapter.
Le reste de cette section est consacrée à la présentation de notre solution en S CL concernant les
connecteurs. Le choix 17 pose les bases de cette solution.
Choix 17 Tout port fourni pf d’un composant c peut être associé à un service particulier du composant
appelé service glue dont l’unique paramètre est une invocation de service. Ce service glue est exécuté
lorsqu’une invocation de service i est reçue par c à travers pf et qu’aucun service fourni à travers pf ne
correspond au sélecteur demandé dans i.
96 Chap 3. Spécification de S CL : un langage à composants minimal
La figure 3.12 et le code source 3.3 illustrent ce choix 17 en reprenant l’exemple précédent de la
figure 3.11 et en utilisant un service glue. Dans cet exemple, un connecteur binaire est utilisé. Il s’agit
d’un composant instance du descripteur M YA D H OC B INARY C ONNECTOR. Ce composant possède le port
fourni Incoming à travers lequel il reçoit des invocations de services et le port requis Outgoing à tra-
vers lequel il émet des invocations de service. Le port Incoming ne fournit aucun service et est associé
à un service glue. Ce service glue est exécuté pour toutes les invocations de service reçue via le port
Incoming puisque qu’aucun service n’est fourni via ce port. L’implémentation de ce service glue est
dédiée à l’adaptation de composants instances des descripteurs PASSWORD M ANAGER et R ANDOM N UM -
BER G ENERATOR .
descriptor MyAdHocBinaryConnector {
providedport Incoming
requiredport Outgoing, IOutGoing
glue Incoming {
# la pseudo variable « si » (service invocation) permet au programmeur
# d’accéder à l’invocation de service courante dans un service glue
return Outgoing.random() * 26
}
}
pm := new PasswordManager
rng := new RandomNumberGenerator
bc := new MyAdHocBinaryConnector
En S CL, il est donc possible d’attacher un service glue à un port fourni d’un composant (primitive
glue) qui est exécuté si le composant ne possède pas le sélecteur demandé. Cette notion de « service
glue » est fortement inspiré de la méthode doesnotunderstand: en Smalltalk qui a d’ailleurs été re-
prise dans de nombreux langages à objets comme Ruby notamment. Cette possibilité d’attacher un
service glue à un port fourni n’est pas spécifique aux connecteurs en S CL mais En S CL, la frontière
3.10. Les connexions doivent-elles être réifiées ? 97
entre composant et connecteur n’est pas structurelle puisque les connecteurs sont des composants
(cf. choix 16) mais vraiment sémantique c’est-à-dire que c’est le rôle joué par un composant dans une
architecture qui détermine s’il est un connecteur.
Dans l’exemple précédent, nous avons défini le descripteur M YA D H OC B INARY C ONNECTOR pour
établir une connexion binaire spécifique aux composants à adapter. Ainsi, cette solution à base de
« services glue », ne permet toujours pas la définition de connecteurs génériques et réutilisables. Dans
l’exemple précédent, seul le service glue est vraiment dépendant des composants PASSWORD M ANAGER
et R ANDOM N UMBERG ENERATOR. Ainsi, nous avons définit des connecteurs réutilisables en S CL tout en
laissant la possibilité à l’architecte de fixer un code glue particulier. Le code source 3.4 illustre cela
en présentant l’instruction bind . . .to . . .glue . . . qui créé une instance du connecteur générique B I -
NARY C ONNECTOR et la paramètre par le code glue spécifié par l’architecte. Ce code glue peut accéder
à l’invocation de service courante via la pseudo-variable si ainsi qu’aux ports du connecteur dans
lequel il sera exécuté. Dans le cas d’un B INARY C ONNECTOR par exemple, le programmeur peut faire
référence aux ports Incoming et Outgoing.
pm := new PasswordManager
rng := new RandomNumberGenerator
# Par défaut, un BinaryConnector est automatiquement créé pour lier deux ports
# lorsque du code glue est spécifié. Dans le code glue, le programmeur peut accéder à :
# - l’invocation courante vi la pseudo variable si
# - aux ports du connecteur dans lequel va s’exécuter le code glue (e.g Outgoing)
bind pm.Randomizer to rng.Generator glue {
return Outgoing.random() * 26
}
Comme nous venons de le voir, il est possible de construire des connecteurs génériques en S CL
pour lesquels le code glue sera spécifié par l’architecte. Jusqu’ici, nous nous sommes essentiellement
concentré sur des connexions binaires (à deux composants) et donc sur le cas du connecteur B INARY-
C ONNECTOR. Toutefois, l’établissement de connexions n-aire (plusieurs composants) nécessite aussi
des connecteurs et des constructions syntaxiques adaptées. Nous proposons ainsi une forme géné-
rale de connecteurs en S CL. Cette forme générale est décrite par le descripteur C ONNECTOR dont un
exemple est donné sur la figure 3.13 et dont la spécification est posée par le choix 18.
Choix 18 Un connecteur est constitué de deux collections de ports nommées Adaptees et Targets. Un
connecteur est en charge de traiter les invocations de services qu’il reçoit à travers ses ports Adaptees en
98 Chap 3. Spécification de S CL : un langage à composants minimal
t1
a1
c : Connector
t2
a2
Adaptees
Targets
t3
En S CL, un connecteur joue le rôle d’adapteur [Gamma et al., 1995] entre les composants connec-
tés à ses ports Adaptees et les composants connectés à ses ports Targets. On peut remarquer que le
connecteur binaire B INARY C ONNECTOR n’est qu’une restriction de cette forme générale n’ayant qu’un
seul port Adaptees nommé Incoming et qu’un seul port Targets nommé Outgoing. De même que
pour le cas binaire, du sucre syntaxique permet de s’affranchir de la définition des descripteurs de
connecteurs dans les cas simples comme le montre le code source 3.5 sur quelques exemples. La pri-
mitive bind . . .to . . .glue . . . a été enrichie pour s’adapter au cas n-aire. Ainsi, lorsque du code glue
est spécifié, il est associé à tous les ports Adaptees du connecteur sauf si un port est explicitement
spécifié.
Dans les approches à composants, un composant est dit composite s’il est constitué de compo-
sants appelés sous-composants (ou composants internes). Ce concept est à la base du mécanisme de
composition (comparable à la relation de composition entre classes en UML).
Tout d’abord : Pourquoi a-t-on besoin de composites ? Pour répondre à cette question, rappelons
que l’expression des services requis d’un composant permet son découplage et augmente ainsi son
potentiel de réutilisation. Cette technique d’extraction des dépendances du code source sous la forme
3.11. De l’intérêt des composants composites ? 99
# Par défaut, un Connector est automatiquement créé pour lier plus de deux ports
# lorsque du code glue est spécifié.
bind a1.r1, a2.r1
to t1.p1, t2.p2, t3.p1
glue {
for i in 1..3
targets[i].perform(si)
}
de services requis est parfois appelée factoring out [Seco et Caires, 2000]. Dans l’exemple de la fi-
gure 3.3, un PASSWORD G ENERATOR peut être utilisé avec n’importe quel autre composant fournissant
un service de génération de nombres aléatoires. Bien que cette technique de factoring out permet un
meilleur découplage, elle pose aussi des problèmes de passage à l’échelle et de réutilisation. En effet,
il est actuellement impossible de réutiliser directement un assemblage de composants, par exemple
un PASSWORD G ENERATOR connecté à un R ANDOM N UMBERG ENERATOR comme présenté sur la figure 3.12.
Cela signifie que pour chaque application dans laquelle l’architecte veut intégrer un PASSWORD G ENE -
RATOR , il devra intégrer un R ANDOM N UMBER G ENERATOR et écrire à nouveau le code glue nécessaire à
la connexion de ces deux composants. Les composites sont notamment une réponse à ces besoins, à
savoir :
– encapsuler plusieurs composants et leur assemblage afin de masquer certains détails dans une
architecture logicielle,
– réutiliser directement des assemblages de composants.
Nous pensons qu’un langage à composants doit permettre la manipulation (définition, réutilisa-
tion, remplacement, maintenance, etc.) d’assemblage de composants. Pour cela, les ADLs ont été les
précurseurs en proposant le concept de configuration. Dans l’ADL W RIGHT par exemple, une confi-
guration représente un ensemble de composants connectés via des connecteurs. Contrairement à
une configuration, un composite est un composant. Les modèles tels que Fractal et ArchJava pro-
posent le concept de composite pour représenter des assemblages de composants. Ces modèles sont
100 Chap 3. Spécification de S CL : un langage à composants minimal
dits hiérarchiques car un composite peut être décomposé en un assemblage de sous-composants in-
terconnectés ; chaque sous-composants pouvant être un composite ou un composant de base. Nous
avons choisi d’intégrer une approche similaire à celle des modèles hiérarchique en S CL.
Choix 20 Un descripteur de composite ne contient pas les descripteurs de ses sous-composants mais il
y fait référence.
Par ce choix 20, nous interdisons l’imbrication des descripteurs en S CL (si l’on fait un parallèle
avec Java, ce choix interdirait les inner classes) . Notre objectif est que tous les descripteurs de com-
posants (même ceux des sous-composants) soit mis sur étagère afin d’être réutilisés. Ce choix est
motivé par le fait qu’un composant ne doit pas uniquement être conçu comme un sous-composant
encapsulé dans un composite donné mais comme un composant mis sur étagère et éventuellement
utilisé en tant que sous-composant.
En S CL comme dans la plupart des approches à composants, un composite contrôle le cycle de
vie (création/destruction) et l’assemblage de ses sous-composants. Les sous-composants sont privés
à leur composite et inaccessibles (voire invisibles) en dehors de son implémentation. La figure 3.14
montre un exemple de composite et le code source 3.6 montre le code du descripteur de ce composite
en S CL. Le service init d’un composant permet son initialisation et est donc utilisé ici pour instancier
les sous-composants. La ligne 10 du code source 3.6 montre l’établissement d’une connexion entre
les deux sous-composants pg et rng. La ligne 15 montre aussi une liaison de ports entre le port fourni
Generator du composite et un port fourni de son sous-composant pg. Nous appelons ces liaisons
3.11. De l’intérêt des composants composites ? 101
entre ports de même nature des liaisons de délégation (cf. définition 10). Syntaxiquement, la même
primitive bind . . .to . . . est utilisée pour les liaisons et les délégations car il n’y a pas d’ambiguïté.
pm : PasswordManager
Liaison de
délégation
1 descriptor CDPasswordManager {
providedport Generator
3
def init {
5 # intanciation des sous-composants
pg := new PasswordGenerator
7 rng := new RandomNumberGenerator
Définition 10 (Liaison de délégation) Liaison orientée entre deux ports de même nature (requis ou
fourni). Les invocations de services reçues par le port origine d’une telle liaison sont transmises sans
modification au port cible de la liaison.
– Comment invoquer les services des sous-composants dans l’implémentation des services du
composite ?
Dans le code source 3.6, les sous-composants sont instanciés dans le service init du composite.
Rappelons que l’instanciation d’un descripteur retourne une référence vers le port nommé default
du composant instancié (cf. choix 8). Les variables pg et rng contiennent donc des références sur
des ports et sont locales au service init. Pour pouvoir faire référence aux ports des sous-composants
dans le code des services du composite, il faudrait stocker ces références dans des « attributs ». Nous
allons en S CL utiliser des ports internes requis (cf. choix 9) à cet effet. La figure 3.15 montre un com-
posite doté du port requis interne Randomizer. Ce port requis interne est lié à un port fourni du sous-
composant rng. Dans le code du service generateACharsOnlyPwd, le service random est invoqué via
ce port interne requis Randomizer. Cette invocation de service est tout à fait standard et obéit aux
mêmes règles que celles décrites dans la section 3.9. En résumé, les sous-composants peuvent être
référencés via des ports internes requis et les services des sous-composants peuvent être invoqués
via le mécanisme standard d’invocation de service. Outre l’uniformité, l’avantage de cette solution
est la possibilité d’utiliser des connecteurs entre un composite et ses sous-composants afin d’adapter
les sous-composants aux besoins du composite à l’aide de code glue.
pm : PasswordManager
generatePwd(size)
generateADigitsOnlyPwd(size)
generateACharsOnlyPwd(size)
init() { ... }
isValid(pwd) { ... }
Checker
generateACharsOnlyPwd(size) {
...
i := Randomizer.random() Randomizer
...
}
isValid(Pwd)
Dans cet exemple présenté à la figure 3.15, le composite exporte trois services. Seul le service
generateACharsOnlyPwd est défini dans le code du composite. Les deux autres services sont four-
nis par le sous-composant pg. Nous expliciterons cela dans la section suivante qui traite en détail
l’invocation de service en tenant compte notamment des liaisons de délégation.
Pour finir, nous souhaitons faire remarquer qu’en S CL les composites ne sont vraiment qu’un
moyen de coupler des composants. La figure 3.16 montre une architecture utilisant les mêmes trois
composants que dans figure 3.15 mais sans composite. Le composant pm de cette architecture n’est
pas un composite car les composants pg et rng peuvent être utilisés par d’autres composants de
l’architecture (ils ne sont pas internes à pm). On peut aussi remarquer que la délégation de port permet
de s’affranchir d’un port requis normalement nécessaire (le port RG est présent dans la figure 3.16
alors qu’il n’existe pas dans la figure 3.15).
3.12. L’invocation de services 103
pm : PasswordManager pg : PasswordGenerator
Generator RG
generatePwd(size)
generateADigitsOnlyPwd(size) rng : RandomNumberGenerator
generateACharsOnlyPwd(size)
Checker
isValid(Pwd)
Randomizer
Contrairement aux langages tels que Julia, ArchJava ou encore ComponentJ, S CL propose un mé-
canisme d’invocation de service (cf. section 3.9) qui n’est pas l’envoi de message car problématique
pour faire de la PPC [Beugnard, 2005] et surtout qui tient compte de l’assemblage des composants.
Dans cette section nous définissons de façon plus précise (que dans la section 3.9) ce mécanisme
central d’invocation de service en tenant compte notamment de l’introduction des connecteurs et
des liaisons de délégation en S CL. Deux cas sont distingués :
– l’émission d’une invocation de service via un port requis,
– la réception d’une invocation de service via un port fourni.
L’algorithme 1 présente le traitement d’une émission d’invocation de service via un port requis.
Cet algorithme comprend trois cas. Le premier (cf. figure 3.17 cas 1) correspond à une liaison stan-
dard entre un port requis et un port fourni. L’invocation est dans ce cas transmise au port fourni lié
et traitée selon l’algorithme 2 (décrit ci-après). Le second cas (cf. figure 3.17 cas 2) correspond à une
liaison de délégation. L’invocation est alors transmise au port requis de délégation qui traite l’invo-
cation selon ce même algorithme 1. Le troisième et dernier cas correspond à une dépendance non
satisfaite puisque le port requis d’émission ne fait l’objet d’aucune liaison. Le traitement de ce cas
d’erreur devrait se traduire par la levée d’une exception. Toutefois, cela sort du cadre de cette thèse
car nécessiterait d’introduire en S CL un système de gestion d’exceptions adapté à la programmation
par composants comme cela a été étudié dans [Souchon, 2005].
cas 1 cas 2
c1 c2 c2
R1 P2 c1
foo() {
...
r1.bar()
... R1 R2
foo() {
} ...
r1.bar()
...
}
Légende :
i : est une invocation bar : est le nom du service invoqué (sélecteur)
r1 : est le port d'émission de i nil : indique qu'il n'y a aucun argument
F IG . 3.17 : Les principaux cas lors de l’émission d’une invocation de service à travers un port requis
L’algorithme 2 présente le traitement de la réception d’une invocation via un port fourni. Cet al-
gorithme distingue quatre cas. Le premier (cf. figure 3.18 cas 1) consiste à délégué l’invocation de
service reçue à un autre port fourni qui traite à son tour l’invocation selon ce même algorithme 3.17.
Dans le second cas (cf. figure 3.18 cas 2), le service demandé est exécuté puisque défini par le com-
posant auquel appartient le port de réception. Dans le troisième cas (cf. figure 3.18 cas 2), c’est le
service glue du port de réception qui est exécuté car le composant ne définit pas le service demandé.
Le dernier cas est un cas d’erreur où le port de réception n’est pas lié, n’est pas délégué et ne possède
pas de service glue associé.
Ces algorithmes ont été conçus pour prendre en compte des cas problématiques ou nécessitant
une attention particulière tels que ceux présentés sur la figure 3.19.
3.13. Faut-il de l’héritage ? 105
F IG . 3.18 : Les principaux cas lors de la réception d’une invocation de service à travers un port fourni
Dans le cas (a), une invocation de service i est reçue via le port p1 du composite c1. Cette in-
vocation est traité selon l’algorithme 2 aboutissant à l’exécution du service foo définit dans le sous-
composant c2 à cause de la liaison de délégation entre les ports p1 et p2. Le service foo du composite
c1 n’est pas exécuté car nous avons choisit de rendre prioritaire la liaison de délégation par rapport à
la présence du service dans le composite. Ce choix permet d’éviter les problèmes de « masquage » qui
surviendraient avec l’autre alternative (priorité au service défini dans le composite par rapport à la
liaison de délégation) si le composite c1 possédait plusieurs sous-composants fournissant le service
foo. En effet, notre solution laisse la possibilité à l’architecte de décider quel composant doit traiter
l’invocation via mise en place ou non de liaisons de délégation. Dans notre exemple, si on enlève la
liaison de délégation entre les ports p1 et p2, la même invocation aboutirai à l’exécution du service
foo du composite c1.
Dans le cas (b), une invocation de service est émise via le port r1 du sous-composant c1 dans
le code du service foo. Le traitement de cette invocation par l’algorithme 1 abouti au cas d’erreur.
Nous n’avons en effet pas intégré de règle permettant d’exécuter automatiquement le service bar du
composite. Une telle règle serait se problématique pour fournir des services différents à deux sous-
composants qui nécessitant un service nommé bar. Dans notre exemple, pour l’invocation du service
bar via r1 dans c1 aboutisse à l’exécution du service bar du composite c2, l’architecte doit établir une
liaison entre le port r1 du sous-composant c1 et le port self du composite c2.
En définitive, nous n’avons intégré en S CL aucune règle implicite en ce qui concerne l’invoca-
tion de service. Les liaisons étant explicitement mise en place par l’architecte, nous les considérons
comme prioritaires.
Dans le monde des objets, la réutilisation repose essentiellement sur les classes et l’héritage
même si de plus en plus de travaux proposent de nouvelles formes de réutilisation [Kiczales et
106 Chap 3. Spécification de S CL : un langage à composants minimal
(a) (b)
c1 c2
c2 c1
P1 P2 R1
foo() { foo() {
... ...
} r1.bar()
...
}
al., 2001; Lahire et Quintian, 2006]. Bien que le mécanisme d’héritage soit universellement uti-
lisé dans les langages à objets, il pose également de nombreux problèmes [Taenzer et al., 1989;
Hürsch, 1994]. Dans cette section, nous discutons les questions suivantes :
– Un mécanisme d’héritage est-il intéressant le monde des composants ?
– Si oui, quelle forme doit prendre ce mécanisme ?
L’héritage est un mécanisme encore largement discuté dans le monde des composants [Weck et
Szyperski, 1996; Szyperski, 2002; Opluštil, 2003; Lahire et al., 2004]. En effet, ce mécanisme semble po-
ser des problèmes allant l’encontre des principes de l’approche à composants (découplage et encap-
sulation). Avant d’expliquer ces problèmes, rappelons que dans les LOO, une classe a deux catégories
de clients :
– ceux qui l’instancient (instantiating clients),
– ceux qui en héritent (inheriting clients).
Ce sont les clients de la deuxième catégorie qui sont à la fois problématiques et puissants. Puis-
sants car ils permettent :
– de faire de la description différentielle via l’ajout de propriétés (attributs et méthodes),
– de redéfinir des méthodes,
– de paramétrer des framework en héritant des classes (éventuellement abstraites) le constituant.
Ces clients sont également problématiques car une sous-classe est un client privilégié de sa super-
classe pouvant accéder à des détails internes « cassant » ainsi l’encapsulation de la super-classe :
« Inheritance breaks encapsulation » [Snyder, 1987]. L’héritage est en effet un mécanisme de réutilisa-
tion « boîte blanche », contrairement à la composition d’objets qui est un mécanisme de réutilisation
« boîte noire », dont l’une des conséquences identifiée est le problème de la classe de base fragile [Mi-
khajlov et Sekerinski, 1998]. La figure 3.20 illustre ce problème. Supposons l’existence d’une classe
Set dont une instance représente un ensemble. Cette classe possède les méthodes : add pour ajouter
un élément et addAll pour ajouter tous les éléments contenus dans un autre ensemble. Supposons
3.13. Faut-il de l’héritage ? 107
que l’on veuille écrire une classe CountingSet en héritant de la classe Set et en ajoutant le code
permettant à un ensemble de maintenir le nombre d’éléments qu’il contient. Pour cela, le program-
meur de la classe CountingSet doit connaître l’implémentation des deux méthodes add et addAll
de la classe Set. En effet, si la méthode add est « appellée » dans le code de la méthode addAll, la
classe CountingSet ne doit redéfinir que la méthode add (cf. figure 3.20 cas (a)). Par contre, si l’im-
plémentation de la méthode addAll n’utilise pas la méthode add et ajoute directement les éléments,
la classe CountingSet doit redéfinir les deux méthodes add et addAll (cf. figure 3.20 cas (b)). Cet
exemple montre que dans de nombreux cas, l’implémentation d’une super-classe doit être connue
pour réaliser une sous-classe.
Set Set
- elements - elements
+ add(o) { ... } + add(o) { ... }
+ addAll(s) { + addAll(s) {
!o"s, self.add(o) !o"s, elements+=o
} }
CountingSet CountingSet
- size - size
+ add(o) { + add(o) {
super.add(o); super.add(o);
size++; size++;
} }
+ addAll(s) {
super.addAll(s);
size+=s.size();
}
L’héritage est donc en contradiction avec les principes de l’approche à composants sur au moins
les deux points suivants :
– il impose d’avoir accès code source (au moins une description fine de l’implémentation),
– il induit un couplage implicite entre une classe et toutes ses super-classes (directes et indi-
rectes) puisque la modification d’une classe modifie l’ensemble de ses sous-classes.
En conséquence, nous pensons que le mécanisme d’héritage entre classes ne peut pas s’appli-
quer directement aux descripteurs de composant à cause de son caractère intrusif. Toutefois, il nous
semble important d’offrir les mêmes possibilités dans un langage à composants que celles offertes
par l’héritage dans un LOO à savoir : la description différentielle, la redéfinition et la réutilisation par
paramétrage. Pour cela, il existe deux catégories d’approches : celles au niveau des descripteurs et
celles au niveau des composants.
108 Chap 3. Spécification de S CL : un langage à composants minimal
Parmi les langages proposant un mécanisme d’héritage au niveau des descripteurs, nous pouvons
citer :
– UML (cf. section 2.2.9) puisque qu’au niveau du méta-modèle UML, la classe Component hérite
de la classe Class, conférant ainsi la possibilité à un Component d’hériter d’un autre Component
ou même d’une Class.
– ArchJava (cf section 2.2.8) permet à une classe de composant d’hériter soit une classe Java stan-
dard, soit une autre classe de composant.
Dans ces deux propositions, le mécanisme d’héritage standard entre les classes est appliqué aux
descripteurs de composant ce qui nous parait être un mauvais choix pour les raisons que nous dé-
taillées ci-dessus. En S CL, nous n’avons pas définit de mécanisme d’héritage au niveau des descrip-
teurs de composants. Nous pensons que cela relève de la problématique de proposer un mécanisme
d’assemblage au niveau des descripteurs. Nous avons déjà évoqué cette problématique dans la sec-
tion 3.3 (discussion autour du choix 3) en précisant qu’il serait intéressant de proposer une version
réflexive de S CL afin d’utiliser les mêmes mécanismes d’assemblages au niveau des composants et au
niveau des descripteurs.
Comme cela a été souligné dans le monde des objets, il est possible d’utiliser la délégation pour
faire de l’héritage [Stein, 1987]. Dans le monde des composants, les composites (comparables à des
objets composites) peuvent donc être utilisés pour de faire de la définition incrémentale, la redéfini-
tion de services et le paramétrage. C’est d’ailleurs ce que proposent, dans une certaine mesure, les
auteurs de ComponentJ [Seco et Caires, 2000]. La figure 3.21 illustre ces possibilités en S CL :
Cas 1 Construire une sous-classe ajoutant de nouvelles propriétés peut se réaliser en créant un com-
posite qui exporte l’ensemble des fonctionnalités de ses sous-composants (cf. cas 1 de la fi-
gure 3.21).
Cas 2 L’héritage est monotone en POO (ajout de propriétés uniquement) alors qu’un composite peut
masquer des fonctionnalités offertes par ses sous-composants. Cela provient du fait qu’un
composite n’est pas forcément un sous-type de tous ses sous-composants.
Cas 3 Une classe avec des méthodes abstraite peut s’apparenter à un composant ayant des services
requis (cf. cas 2 de la figure 3.21).
Cas 4 La redéfinition d’une méthode dans une sous-classe peut être en partie réalisé par la création
d’un composite. Attention, les invocations dans les sous-composants ne profitent pas de la re-
définition comme c’est le cas pour les super-classes (cf. cas 3 de la figure 3.21). On aurait pu
mettre en place ce mécanisme en S CL via des liens de délégation implicites entre les ports self
des sous-composants et le port self du composite (cf. section 3.12). Toutefois, cela poserait le
« problème de sous-composants fragiles » de façon analogue à celui de la classe de base fragile.
Si le concepteur d’un composant veut laisser la possibilité de redéfinir un service, il doit le faire
explicitement via l’utilisation d’un service requis.
3.13. Faut-il de l’héritage ? 109
b:B
A a:A
+ foo() Default
foo(){...}
Cas 1
B foo()
bar() { ... }
+ bar() bar()
glue Default {
ra
return ra.perform(si)
}
a:A
<<abstract>> Default Ra
Cas 2 A bar() { ... }
+ abstract foo()
+ bar()
bar() foo()
b:B
A
+ foo() a:A
Default
+ bar() {
foo(){...}
self.foo() bar() {
} self.foo()
Cas 3 }
foo()
bar()
foo() { ... } le service foo
ra glue Default {
B return ra.perform(si) de a est
+ foo()
} invoqué
3.14 Synthèse
3.15 Bilan
Dans ce chapitre, nous avons présenté une argumentation concernant la conception d’un lan-
gage à composants. Nous avons commencé par définir notre vision d’un langage à composants ainsi
que nos objectifs pour réalisation un tel langage. Nous avons ensuite détaillé de façon ordonnée, les
problèmes que nous avons rencontrés lors de la spécification de notre langage à composants nommé
S CL (Simple Component Language). Pour chaque problème, nous avons décrit les principales solu-
tions existantes et présenté ensuite notre solution pour S CL ainsi que les raisons qui ont conduites à
intégrer cette solution particulière. Ainsi, nous pensons que S CL propose des abstractions et des mé-
canismes adaptés à la programmation par composants ce qui n’est pas toujours le cas des langages
comme Julia, ArchJava ou ComponentJ comme nous avons essayé de le montrer tout au long du cha-
pitre. La section 3.14 fait la synthèse de toutes les constructions du langage S CL. Rappelons toutefois
les points qui font la spécificité du langage S CL :
– l’intégration uniforme des objets de base qui sont considérés comme des composants,
– le mécanisme d’invocation de services et notamment celui du passage d’arguments en terme
de connexions temporaires préservant ainsi l’intégrité des communications,
– le port interne nommé self permettant de faire des auto-références en utilisant l’invocation
de service standard,
– la possibilité d’associer un service glue à un port fourni et la standardisation des connecteurs
qui facilitent la résolution de conflits lors de l’assemblage des composants.
4
CHAPITRE
Séparation des préoccupations en S CL
Préambule
S CL doit permettre la définition de composants réutilisables dans différents contextes pour lesquels
ils n’ont pas été spécifiquement conçus. La modularisation des préoccupations que cela suppose est
actuellement difficile à réaliser que ce soit avec une approche à composants ou avec une approche à
objets. Ce chapitre a donc pour objectif de proposer une solution en ce qui concerne la séparation des
préoccupations en S CL. Pour cela, nous commençons par présenter le principe de séparation des préoc-
cupations. Nous décrivons ensuite la programmation par aspects (PPA ou Aspect-Oriented Program-
ming soit AOP en anglais) qui est la solution permettant de mettre en œuvre ce principe dans le monde
des objets grâce à l’introduction du concept d’aspect et du mécanisme de ( tissage) notamment. Nous
étudions ensuite les principales extensions des approches à composants qui ont intégré des idées issues
de la PPA et nous proposons enfin deux extensions de S CL permettant la séparation des préoccupa-
tions. La première permet à un même composant S CL d’être utilisé de façon standard ou transversale
grâce à une extension du concept de « port » et à l’ajout de nouveaux types de liaisons. La deuxième
permet à l’architecte de réaliser des connexions basées sur les changements d’états des composants en
affranchissant le programmeur de ces composants de l’écriture du code normalement indispensable
pour réaliser ce type de connexion. Finalement, la sixième section conclut ce chapitre dans lequel nous
avons développé les idées décrites dans [Fabresse, 2004; Fabresse et al., 2004; Fabresse et al., 2006a;
Fabresse et al., 2006b].
113
114 Chap 4. Séparation des préoccupations en S CL
L
E principe de séparation des préoccupations (separation of concerns) [Parnas, 1972; Dijkstra, 1976]
préconise le découpage d’une application en entités de taille plus réduite et aussi indépen-
dantes que possible les unes des autres afin de faciliter la maintenance, la compréhension et la réuti-
lisation. Cette modularisation du logiciel est toutefois difficile à mettre en place notamment pour
certaines préoccupations qui ne peuvent être encapsulées dans des entités logicielles telles que des
objets ou des composants comme la gestion des logs (journaux), des transactions ou encore la sé-
curité (identification, droits d’accès, etc.). Ces préoccupations ne relèvent généralement pas du do-
maine métier de l’application et elles sont désignées par le terme de préoccupations transversales (en
anglais, cross-cutting concerns). Le code des préoccupations transversales est souvent noyé dans le
code fonctionnel et distribué sur l’ensemble du programme notamment à cause de sa nature trans-
versale. Dans la suite de cette section, nous présentons la programmation par aspects [Kiczales et al.,
1997], originellement proposée comme une extension de la programmation par objets, qui propose
une solution face à ce problème.
La PPA propose notamment la notion d’aspect permettant d’encapsuler ces préoccupations
transversales et le mécanisme de tissage pour intégrer le code des aspects et le code métier. La fi-
gure 4.1 détaille le principe général de la programmation par aspects. Les applications construites en
utilisant les approches à objets ou à composants possèdent une architecture pouvant se résumer à
du code métier (fonctionnel) qui utilise du code technique (non-fonctionnel). Par exemple la jour-
nalisation (logging) est typiquement une préoccupation transversale qui nécessite que le code mé-
tier fasse explicitement appel au code technique de journalisation pour chaque élément nécessitant
d’être journalisé. La PPA propose d’encapsuler le code technique dans des aspects qui seront ensuite
tissés avec le code métier — lequel ne contenant aucune référence explicite au code technique — afin
de produire une nouvelle application intégrant la fonctionnalité technique.
Application
Application
Code Application
métier
(OOP ou COP) + AOP Code
tissage
métier technique
Aspect(s)
Code
technique
Dans cette section, nous allons présenter les principaux concepts et mécanismes de la PPA à tra-
4.1. Présentation de la programmation par aspects 115
vers le langage AspectJ [Kiczales et al., 2001]1 qui est une référence dans ce domaine et un bon moyen
d’illustrer la PPA. Cette présentation est synthétique et ne couvre pas tout le domaine de la PPA [Kic-
zales et al., 1997], ni tout le langage AspectJ 2 .
Un point de jonction (Join Point) est un point précis et bien défini dans l’exécution d’un pro-
gramme où il est possible d’exécuter du code transversal. Les principaux points de jonction dispo-
nibles en AspectJ sont les suivants :
– method call : point de jonction lors d’un envoi de message particulier,
– method execution : point de jonction lorsque le code d’une méthode est exécuté,
– constructor call / execution : points de jonction analogues aux points de jonction précédents
mais pour les constructeurs,
– field reference : point de jonction lors de l’accès à un attribut,
– field set : point de jonction lors de la modification de la valeur d’un attribut.
À tout point de jonction sont associés trois éléments : l’objet courant (this), l’objet cible (target)
et un tableau d’arguments (args).
pectWerkz (http://aspectwerkz.codehaus.org).
2 http://www.eclipse.org/aspectj/
116 Chap 4. Séparation des préoccupations en S CL
pointcut change():
call( void Counter.setValue(int) ) ||
call( void Counter.setPas(int) );
Il est possible d’utiliser des « caractères joker » (wildcards) pour définir des points de coupes.
Le listing 4.2 illustre cela en définissant le point de coupe change comme l’ensemble des points de
jonction correspondants aux appels des méthodes publiques de la classe Counter dont le type de
retour est void, le sélecteur commence par set et les arguments sont quelconques.
pointcut change():
call( public void Counter.set*(..) );
4.1.3 Advice
Un advice associe un point de coupe à du code qui sera exécuté à chaque point de jonction défini
par le point de coupe. On peut assimiler un advice à une sorte de méthode définissant le code non-
fonctionnel qui sera exécuté lors de certains points de jonctions définis par un point de coupe. En
AspectJ, cinq sortes d’advices sont disponibles3 :
– before advice est exécuté avant l’exécution du point de jonction, par exemple avant le début de
l’exécution de la méthode s’il s’agit d’un point de jonction method call,
– after advice est exécuté après l’exécution du point de jonction (cf. listing 4.3). Par exemple, dans
le cas d’un point de jonction method call, l’advice est exécuté après l’exécution du corps de la
méthode, juste avant que le flot de contrôle ne retourne à l’appelant. Un after returning advice
correspond à un retour « normal » de la méthode, un after throwing advice correspond à un
retour exceptionnel de la méthode par la levée d’une exception et un after advice correspond
au retour de la méthode qu’il soit « normal » ou exceptionnel.
– around advice est exécuté à la place du point de jonction et peut éventuellement déclencher
l’exécution du point de jonction.
after() : change() {
System.out.println("A Counter has changed");
}
3 Les advices ne sont pas sans rappeler le mécanisme de combinaison de méthodes en CLOS [Bobrow et al., 1986] lui-
même provenant des démons dans les langages à frames tel que Flavors [Moon, 1986].
4.1. Présentation de la programmation par aspects 117
Une déclaration inter-type (inter-type declaration aussi appelée introduction) consiste à déclarer
des attributs et des méthodes en dehors de la hiérarchie des classes (dans un aspect comme nous
le verrons dans la suite) et rattachés ensuite à des classes existantes. Contrairement aux advices, les
déclarations inter-type sont traitées statiquement à la compilation et permettent de modifier la struc-
ture des classes en ajoutant des attributs ou des méthodes mais aussi en modifiant le graphe d’hé-
ritage c’est-à-dire la super-classe et les super-interfaces. Le listing 4.4 montre un exemple d’aspect
modifiant la structure de la classe Counter afin d’en faire un Javabeans (cf. section 2.2.3). Pour cela,
la super interface Serializable est ajoutée ainsi que des méthodes de gestion d’observateurs.
aspect CounterObserving {
declare parents: Counter implements Serializable;
Ce mécanisme est à rapprocher du mécanisme d’héritage puisqu’il offre les mêmes possibilités, à
la différence que la classe existante est modifiée lors de la compilation au lieu de créer une nouvelle
sous-classe. Modifier la classe existante permet à toutes les autres classes utilisant la classe modifiée
de profiter des ajouts. Ce mécanisme est aussi sujet à de nombreux conflits [Kiczales et al., 2001].
4.1.5 Aspect
Un aspect est une unité de modularité encapsulant une préoccupation transversale. Comme nous
l’avons vu précédemment, la déclaration d’un aspect contient des déclarations inter-type, des advices
et des points de jonction. Le listing 4.5 montre le code de l’aspect CounterObserving complet inté-
grant les advices.
En AspectJ, un aspect est similaire à une classe, sans toutefois être une classe. Un aspect peut
étendre d’autres aspects et même une classe ou encore implémenter une interface. Les aspects sont
instanciés non pas directement via l’opérateur new mais automatiquement.
118 Chap 4. Séparation des préoccupations en S CL
aspect CounterObserving {
declare parents: Counter implements Serializable;
L ISTING 4.5 : Exemple de transformation d’une classe Java Counter en un composant Javabeans avec
AspectJ
4.1. Présentation de la programmation par aspects 119
4.1.6 Tissage
Il s’agit du processus visant à combiner le programme de base (code métier) constitué d’objets
ou de composants avec les aspects afin de produire une application étendue avec les comportements
définis par les aspects. On distingue généralement le tissage statique (à la compilation) et le tissage
dynamique (à l’exécution). AspectJ effectue le tissage statiquement lors de la compilation. L’applica-
tion compilée résultante intègre le code métier et celui des aspects. La phase de tissage doit faire face
à des conflits [Sanen et al., 2006; Pulvermüller et al., 2001] (ce problème est aussi appelé feature inter-
action problem) lorsque différents aspects sont à mettre en œuvre pour un même point de jonction.
Différentes politiques de résolution des conflits sont possibles, depuis une résolution manuelle jus-
qu’à une résolution automatique par le tisseur en fonction de critères d’ordonnancement des aspects
par exemple.
Finalement, la programmation par aspects peut se résumer ainsi [Filman et Friedman, 2000] :
« AOP can be understood as the desire to make quantified statements about the behavior
of programs, and to have these quantifications hold over programs written by oblivious
programmers. »
Un modèle de points de jonction qui définit les points de jonctions disponibles dans un pro-
gramme. Ce modèle est fondamental puisque les points de jonction déterminent les points
d’adaptations. On distingue généralement les points de jonction statiques et les points de jonc-
tion dynamiques qui dépendent de valeurs uniquement connues lors de l’exécution [Stoerzer
et Hanenberg, 2005].
Un langage de coupe permettant de sélectionner un ensemble de points de jonctions. Les langages
de coupe doivent proposer des constructions permettant de sélectionner des points de jonc-
tions statiques ou dynamiques. Le langage de points de coupe permettant de désigner des
4 Certainement en référence aux frameworks.
120 Chap 4. Séparation des préoccupations en S CL
points de jonction d’AspectJ est relativement simple puisqu’il s’agit essentiellement de patrons
syntaxiques et d’opérateurs booléens.
Un modèle d’aspects qui définit la structure d’un aspect. Par exemple, en AspectJ les aspects
contiennent des définitions inter-types, des advices et des points de coupe. Cette définition rend
les aspects en AspectJ peu réutilisables puisqu’ils contiennent des points de coupes dont la dé-
finition est généralement très liée à l’application de base. Il existe d’ailleurs des travaux qui ont
proposé de séparer d’un côté, la définition des aspects et de l’autre, l’application métier afin
d’augmenter leur réutilisation [Lieberherr et al., 1999].
Un tisseur est un compilateur ou un interprète qui mélange un programme de base et des aspects
pour produire un programme qui met en œuvre de multiples préoccupations. Le tissage peut
être effectué statiquement ou dynamiquement. Lorsqu’il est réalisé statiquement, il est généra-
lement impossible d’enlever ou ajouter dynamiquement des aspects. Dans tous les cas, il doit
permettre la résolution de conflits soit en offrant au programmeur la capacité de les résoudre,
soit en intégrant une politique de résolution automatique.
De même que les approches à objets, les approches à composants souffrent d’insuffisances en ce
qui concerne la modularisation des préoccupations transversales [Lieberherr et al., 1999; Duclos et
al., 2002]. Dans la suite de cette section, nous présentons divers travaux concernant l’intégration des
aspects et des composants. Nous présentons ces travaux en fonction des approches à composants sur
lesquelles ils sont basés :
– des approches à composants réparties comme CCM ou EJB,
– des modèles de composants (souvent académiques) comme Fractal,
– des langages à composants (au niveau du code source) comme FuseJ.
Comme nous l’avons vu dans la section 2.1.3, les approches à composants réparties reposent sur
des intergiciels qui fournissent des services non-fonctionnels comme la persistance, les transactions
et la sécurité. Ces services non-fonctionnels sont des fonctionnalités transversales qui ne peuvent
être encapsulées par des composants. Il s’agit d’aspects que le programmeur peut mettre en œuvre
soit déclarativement dans le descripteur de déploiement soit par programmation grâce aux méthodes
fournies par le framework d’implémentation d’un composant. Les composants n’accèdent pas direc-
tement aux services non-fonctionnels, sinon le code d’accès serait mélangé au code métier. Pour cela,
les communications entre la couche métier et la couche technique sont contrôlées par une tierce en-
tité au sein de la plateforme intergicielle : le conteneur de composant qui utilise le code métier lorsque
cela est nécessaire et non l’inverse. Ce principe d’externalisation, parfois appelé inversion de contrôle
(inversion of control), est tout à fait similaire au fonctionnement des frameworks où le code écrit par
un programmeur est appelé par le code du framework.
4.2. Aspects et approches à composants 121
La réalisation de ces fonctionnalités transversales dans les approches à composants réparties est
assez rigide et limitative. Les principales insuffisances de ces modèles sont les suivantes :
– Les fonctionnalités transversales sont souvent simples et limitées. Par exemple, le modèle de
sécurité de J2EE est basé sur le contrôle d’accès à l’aide de rôles mais il ne propose pas d’héri-
tage de droits d’accès entre un rôle et un sous-rôle.
– Le paramétrage des fonctionnalités transversales est basé sur l’extension de framework par hé-
ritage comme en J2EE, ce qui limite les extensions possibles aux méthodes déclarées par le
framework.
– L’ajout de nouvelles fonctionnalités transversales est souvent impossible ou alors nécessite de
modifier directement l’implémentation de l’intergiciel.
De nombreux travaux proposent d’utiliser des techniques de la programmation par aspects pour
prendre en compte ces fonctionnalités transversales dans les approches à composants réparties et
pallier ces limitations. Parmi les extensions des approches à composants réparties avec des aspects,
on peut citer AspectJ2EE [Cohen et Gil, 2004], JBossAOP [Pawlak et al., 2004], AES [Choi, 2000] ou
encore CVM [Duclos et al., 2002] qui sont des extensions du modèle EJB intégrant des possibilités
offertes par les approches à aspects. Une comparaison de ces principales approches a été réalisée
dans [Oussalah, 2005] et nous en rappelons ici les éléments essentiels :
– L’approche boîte noire est conservée et un aspect ne peut modifier directement l’implémen-
tation d’un composant (contrairement à AspectJ qui peut modifier l’implémentation d’une
classe) cela afin de garantir la possibilité d’utilisation d’un composant développé par un tiers.
Ainsi, les modèles de coupes n’autorisent généralement pas les définitions inter-types ou alors
de façon limitée comme c’est le cas en JBossAOP avec des mixins.
– La plupart de ces approches sont basées sur l’extension du conteneur de composants. En ef-
fet, le conteneur est l’entité privilégiée pour l’intégration des aspects puisqu’il traite toutes les
requêtes émises et reçues par un composant. Le modèle de coupes proposé est donc géné-
ralement basé sur l’interception des appels de méthodes par le conteneur. Ainsi, les modèles
de coupes supportent tous au minimum les appels de méthodes (call en AspectJ) puisqu’ils
ne remettent pas en cause le modèle boîte noire. Certaines approches proposent toutefois des
coupes spécifiques. Par exemple, AspectJ2EE permet de spécifier une coupe spécifique pour
les invocations de méthodes distantes via le mot-clef remotecall (en plus de celle similaire à
call en AspectJ pour les appels locaux) qui permet de définir le traitement d’un appel à la fois
du côté du client et du côté du serveur, c’est-à-dire à différents niveaux de l’application (tier-
cutting).
– Le tissage prend des formes variées dans ces approches. Toutefois, elles ont toutes pour objec-
tifs de préserver la compatibilité avec le support d’exécution standard du modèle industriel de
composants sous-jacent. C’est ainsi que les techniques de tissage utilisées s’appuient sur des
mécanismes comme la génération d’encapsulateurs (wrappers). Par exemple, en AspectJ2EE
le tissage est effectué lors du déploiement par génération de sous-classes des classes d’implé-
mentation de l’EJB.
Toutes les approches présentées dans cette section sont construites à partir des modèles de com-
posants dits industriels et se spécialisent dans le domaine des applications réparties dont les spéci-
ficités contraignent la mise en œuvre de l’approche à aspects. De plus, ils sont pour la plupart basés
122 Chap 4. Séparation des préoccupations en S CL
sur des approches de type canevas qui ne satisfait pas complètement le besoin de non-anticipation.
Dans la section suivante, nous présentons d’autres approches construites par extension de modèles
de composants académiques comme Fractal.
Des extensions de modèles de composants (comme Fractal par exemple) sont actuellement pro-
posées afin d’offrir une meilleure modularisation des préoccupations transversales. Dans la suite de
cette section, nous détaillons deux approches qui nous paraissent représentatives de cette volonté
d’intégrer les concepts et mécanismes de l’approche à aspects dans un modèle de composants en
l’occurrence Fractal.
Fractal-AOP
Fractal-AOP [Fakih et al., 2004] propose une extension du modèle de composants Fractal afin de
supporter la PPA. Cette extension repose sur l’ajout de deux nouvelles interfaces de contrôle cEC et
sIC, de composants de tissage et de composants d’advice (cf. figure 4.2).
a b
Application de base
Composant
fonctionnelle fonctionnelle
cEC cEC
logger security
Examinons tout d’abord les deux nouvelles interfaces associées aux composants métiers a, b et c
dans la figure 4.2 :
– Client Execution Controller (cEC) qui est une interface de contrôle cliente donnant accès aux
occurrences des points de jonction dans le flot d’exécution du composant. En effet, à chaque
point de jonction, la membrane du composant va émettre une invocation d’opération via
cette interface afin que les composants d’aspects, décrits ci-dessous, puissent intervenir et ef-
fectuer leurs traitements. Par exemple, cette interface déclare les deux opérations suivantes :
receiveMessage qui définit un point de jonction lors de la réception d’un message par le com-
posant à travers l’une de ses interfaces de rôle serveur et connect qui définit un point de jonc-
tion lors de la connexion d’une interface du composant.
– Server Introduction Controller (sIC) qui est une interface serveur permettant aux aspects
d’effectuer des introductions, c’est-à-dire des changements transversaux statiques modifiant
la structure d’un composant. Par exemple, cette interface déclare les opérations suivantes :
addInterface qui permet d’ajouter une interface à un composant ou encore removeMessage
qui permet d’enlever une opération d’une interface d’un composant. Bien évidemment, les in-
troductions nécessitent de vérifier les liaisons déjà établies pour s’assurer de leur validité après
modification.
En résumé, les points de jonctions disponibles en Fractal-AOP [Fakih et al., 2004] sont :
– écriture/lecture des attributs,
– connexion/déconnexion de deux composants,
– ajout/suppression d’un sous-composant à un composite,
– changement de l’état de cycle de vie d’un composant,
– ajout/suppression d’un attribut/opération/interface au composant,
– réception d’un message sur les interfaces de rôle serveur,
– émission d’un message sur les interfaces de rôle client.
Un aspect est mis en place via des composants d’advice et des composants de tissage. Un com-
posant d’advice définit les traitements d’un aspect particulier à l’aide de composants primitifs ou
composites. Un composant de tissage met œuvre le tissage entre les composants métiers et les com-
posants d’advice, c’est-à-dire qu’il gère les définitions de coupes, les règles de tissage et est en charge
de l’invocation des opérations des composants d’advice. Pour cela, un composant de tissage possède
une interface fonctionnelle sEC (Server Execution Controller) sous-type de l’interface cliente cEC à
travers laquelle il reçoit des invocations d’opération correspondant aux points de jonctions définis
dans l’interface cEC des composants métiers connectés. Le composant de tissage peut alors filtrer les
points de jonctions et déclencher les traitements des composants d’advice si nécessaire.
Dans le cas des composites, les interfaces sIC et cEC sont enrichies et proposent des points de
jonction spécifiques permettant de déterminer le sous-composant auquel ils se rattachent. Les inter-
faces cEC des sous-composants peuvent être liées (au choix de l’architecte) à celle du composite, de
même que l’interface sIC peut être liée à celle des sous-composants.
Les conflits de tissage sont résolus par l’architecte qui peut définir un ordre entre les composants
de tissage en les liant via leurs interfaces sEC et cEC. Cette solution est inspirée du schéma de concep-
tion « chaîne de responsabilité » [Gamma et al., 1995]. Dans la figure 4.2, le composant de tissage
124 Chap 4. Séparation des préoccupations en S CL
c t 1 directement connecté au composant métier est prioritaire par rapport au composant de tissage
suivant dans la chaîne c t 2 connecté à c t 1 via leurs interfaces respectives sEC et cEC.
Critique. Les avantages de cette solution sont d’une part sa simplicité et d’autre part la compa-
tibilité avec le modèle d’exécution de Fractal puisqu’il n’y a pas de nouvelles abstractions ou de
mécanismes spécifiques. Fractal-AOP propose un modèle de points de jonction qui est un modèle
« boîte blanche » autorisant d’une part les introductions et même les points de coupe sur les sous-
composants d’un composite. Le choix d’un modèle boîte blanche s’explique certainement par le fait
qu’un composite peut exporter son contenu en Fractal. En Fractal-AOP, les aspects sont construits
à partir de composants d’advice et de tissage mais ne sont pas des composants. Ce choix de ne pas
réifier les aspects sous la forme de composites est volontaire de la part des auteurs qui s’interrogent
sur les implications d’un tel choix notamment du point de vue de la répartition des aspects.
FAC [Pessemier et al., 2006] est aussi une extension du modèle de composants Fractal qui vise à
intégrer le développement par aspects et le développement par assemblage de composants. En FAC,
tous les concepts de la PPA sont modélisés en utilisant ceux de l’approche à composants. FAC dis-
tingue les éléments suivants (cf. figure 4.3) : composant aspectisable (aspectisable component), com-
posant d’aspect (aspect component), liaison d’aspect (aspect binding) et domaine d’aspect (aspect do-
main).
Tout d’abord, les points de jonction supportés par FAC sont les réceptions et les émissions d’ap-
pels d’opérations à travers les interfaces des composants aspectisables. Les composants aspectisables
sont des composants métier qui peuvent être tissés avec des aspects (composants A, B et C sur la fi-
gure 4.3). Par rapport à des composants Fractal standards, les composants aspectisables fournissent
une interface nommée WI (Weaving Interface) contenant les opérations permettant le tissage du com-
posant comme setAspectBinding qui permet de tisser un composant d’aspect à un point de coupe
donné du composant ou encore unsetAspectBinding qui effectue l’opération inverse.
Un composant d’aspect encapsule une préoccupation transversale. Le listing 4.6 montre un
exemple de définition de composant d’aspect qui correspond au composant ac1 de la figure 4.3. Il
s’agit d’un composant Fractal standard fournissant une interface fonctionnelle serveur nommé ACI
(Aspect Component Interface) fournissant l’opération invoke. Cette opération prend en paramètre un
point de jonction et elle définit un around advice c’est-à-dire le code à exécuter avant et après ce point
de jonction. Contrairement à AspectJ, FAC ne supporte que les around advice qui sont finalement les
plus généraux puisqu’un advice de type before ou after peut être mis en place avec un around advice.
En Fractal, les composants sont liés via leurs interfaces clientes et serveurs. En FAC, ces liaisons
sont appelées liaisons régulières pour les différencier des liaisons d’aspects qui permettent de lier un
composant via son interface WI à un composant d’aspect via son interface ACI. Ces liaisons d’as-
pects n’obéissent pas aux mêmes contraintes que les liaisons régulières comme le sous-typage des
interfaces client et serveur et n’offrent pas la même sémantique puisque dans le cas d’une liaison
d’aspect, l’opération invoke de l’interface ACI est appelée pour chaque point de jonction défini lors
de la liaison.
4.2. Aspects et approches à composants 125
Itf. serveur de
sWI sWI Légende : contrôle
Application de base
« niveau métier »
Itf. cliente de
sWI contrôle
Interfaces :
c sWI : Interface serveur de tissage (Weaving)
sACI : Interface serveur AspectComponent
Composants d'aspects
<<interface>> <<interface>>
WI ACI
niveau « tissage »
setAspectBinding(...) invoke(FCMethodInvocation)
unsetAspectBinding(...)
ac1 ac2 weaving(...)
sACI unweaving(...)
sACI
changeACOrder(...)
...
domaine d'aspect
@AspectComponent
public class AC1 implements AspectComponent {
// AC1 possède une interface client nommée logger
@Requires(name = "logger")
private ILogger logger;
Un domaine d’aspect est la réification sous la forme d’un composite du domaine d’application
d’un composant d’aspect. Ce composite est créé automatiquement et il contient un composant d’as-
pect ainsi que tous les composants métiers tissés avec ce composant d’aspect. Un composant métier
peut appartenir à plusieurs domaines d’aspect puisque le modèle Fractal supporte le partage de sous-
composants. Les conflits de tissage sont résolus localement pour chaque composant aspectisable via
l’interface WI qui fournit le service changeACOrder. Ce service permet d’ordonner les composants
d’aspects tissés sur un composant aspectisable.
FAC propose aussi une extension de l’ADL Fractal (langage XML) afin de supporter la définition
des liaisons d’aspect et la définition des coupes via des expressions régulières comme le montre le lis-
ting 4.7. La balise weave décrit le tissage du composant d’aspect ac1 avec chaque sous-composant du
composite monApplication (rootComp="this") de sorte que les opérations reçues par les interfaces
serveurs de ces sous-composants soient interceptées (attribut pcd). Le domaine d’aspect est nommé
logDomain.
Critique. FAC propose une intégration des aspects et des composants en s’intéressant particulière-
ment à l’application des concepts issus des composants aux concepts de la PPA. C’est ainsi qu’une
préoccupation transversale est encapsulée dans un composant d’aspect, le domaine d’application
d’un aspect est aussi réifié sous la forme d’un composite et les liaisons d’aspect représentent les liens
de tissage entre les composants d’aspects et les composants aspectisables. Contrairement à Fractal-
AOP, FAC propose un modèle de point de jonction de type boîte noire et a introduit un nouveau type
de liaison d’interfaces.
4.2. Aspects et approches à composants 127
Dans cette section, nous présentons les approches à composants et à aspects, au niveau des
langages de programmation. Il existe une multitude d’approches visant à combiner les approches
aspects et composants au niveau des langages de programmation comme Jiazzi [McDirmid et al.,
2001b], Aspectal Collaborations [Lieberherr et al., 2003], Caesar [Mezini et Ostermann, 2003] ou en-
core FuseJ [Suvée et al., 2005; Suvée et al., 2006]. Nous présentons dans la suite de cette section le
langage FuseJ qui est le plus récent et aussi le plus proche de S CL. Une étude incluant Jiazzi [McDir-
mid et al., 2001b], Aspectal Collaborations [Lieberherr et al., 2003] et Caesar [Mezini et Ostermann,
2003] (mais pas FuseJ) est disponible dans [Oussalah, 2005].
En FuseJ, un composant est constitué de gates (ensemble de méthodes) fournies ou requises. La
connexion de composants est réalisée via des connecteurs et des linklets (liaisons) entre les gates. Dif-
férentes sortes de linklets existent : les regular (requis/fournis ou délégation) et les crosscutting (be-
fore, after, around) qui correspondent aux advices sur les appels de services. La figure 4.4 montre une
architecture d’application intégrant des composants et des aspects en FuseJ. La listing 4.8 présente la
déclaration de la connexion entre les composants A, C et L OGGER.
MonApp
A B
C
before
Légende :
Log Logger
Gate
Linklet
Les auteurs de FuseJ n’abordent pas les problèmes de conflits. Par exemple, est-il possible d’in-
tégrer deux linklets de type before sur une même gate ? Et dans l’affirmative, quelle est leur priorité
respective ?
Critique. L’approche proposée par FuseJ est similaire à celle de FAC dans le sens où de nou-
veaux types de liaisons sont introduits. De plus, ces nouveaux linklets sont comparables aux compo-
sants d’aspects de FAC. Toutefois, FuseJ semble moins avancé que FAC ou Fractal-AOP actuellement
puisque les conflits ne sont actuellement pas traités.
128 Chap 4. Séparation des préoccupations en S CL
Comme nous l’avons montré jusqu’ici, les approches à aspects et à composants peuvent être in-
tégrées afin d’apporter une meilleure modularisation des préoccupations transversales dans les ap-
plications construites par assemblage de composants. Dans cette section, nous présentons la façon
dont S CL a été étendu afin de supporter la PPA sans pour autant remettre en question les choix que
nous avons effectués dans le chapitre 3. Chaque sous-section qui suit est une discussion visant à dé-
terminer s’il est nécessaire d’intégrer un nouvel élément en S CL et dans l’affirmative, de choisir sous
quelle forme faire cette intégration afin de permettre la séparation des préoccupations transversales.
Boîte noire ou boîte blanche ? AspectJ et Fractal-AOP proposent un modèle de points de jonction
de type boîte blanche. Avec un tel modèle de points de jonctions, les aspects peuvent être tissés de
façon précise et fine dans une application de base en accédant par exemple aux sous-composants
des composites, aux attributs privés, etc. Il est même possible de modifier la structure des éléments
grâce aux introductions notamment. Bien évidemment, un modèle boîte blanche suppose l’accès au
code source de l’application de base. Les coupes deviennent alors très dépendantes de l’application
pour laquelle elles sont définies ce qui peut réduire les possibilités de réutilisation des aspects lorsque
leur définition intègre des définitions de coupe. A contrario, AspectJ2EE ou encore FAC proposent des
modèles de points de jonction de type boîte noire. Ces modèles ne permettent à un aspect d’accéder
directement à l’implémentation des composants constituant l’application de base. Cette dernière ap-
proche, respectueuse de l’encapsulation et du découplage, semble mieux adapté au développement
par composants où un composant doit pouvoir être remplacé par un autre composant offrant les
fonctionnalités adéquates indépendamment de son implémentation. C’est pourquoi nous avons fait
le choix suivant pour S CL :
Non-anticipation. Comme nous l’avons indiqué dans la section 4.1.7, la propriété de non-
anticipation (obliviousness) est importante. Toutefois, elle n’est pas supportée par toutes les ap-
proches comme c’est le cas pour les approches à composants réparties. Fractal-AOP et FAC ne
semblent pas supporter complètement cette propriété. Pour être « aspectisables », les composants
métiers doivent posséder les interfaces cEC et sIC dans le cas de Fractal-AOP et l’interface sWI dans
le cas de FAC. Si un programmeur doit décider de doter son composant d’interfaces particulières afin
qu’il puisse supporter le tissage, on peut considérer que la propriété de non-anticipation est violée.
Par contre, si tous les composants sont (automatiquement) dotés d’une interface de tissage sans que
le programmeur n’ait à s’en soucier ni à écrire de code spécifique pour cela, la non-anticipation n’est
pas violée. Cette propriété de non-anticipation nous semble cruciale, aussi bien pour le développe-
ment des composants (cf. objectif 3) que pour l’intégration des aspects.
Choix 22 Tous les composants peuvent être tissés indépendamment de leur conception et de leur im-
plémentation.
Finalement, en considérant les deux choix précédents, nous avons intégré les points de jonction
suivants en S CL :
– les invocations de services à travers un port,
– la connexion/déconnexion d’un port de composant.
En S CL, les ports étant les uniques points d’accès d’un composant, ils sont donc les candidats
idéaux pour supporter un modèle de points de jonction de type boîte noire. De plus, un composant
possède toujours des ports et est donc toujours « aspectisable » (respect de la non-anticipation). Les
réceptions d’invocation de service via les ports fournis et l’émission d’invocations de service via les
ports requis sont les éléments minimum d’un tel modèle. Nous avons aussi intégré les points de jonc-
tion correspondant à la connexion et la déconnexion d’un port présents en Fractal-AOP. Au niveau des
advices, S CL supporte les advices de type before et after qui peuvent être combinés afin de supporter
ceux de type around, contrairement à FAC qui supporte uniquement les advices de type around. Ce
choix n’est absolument pas restrictif ni pour S CL, ni pour FAC puisque les possibilités sont équiva-
lentes. Nous verrons dans la suite que cela permet de s’affranchir en S CL de déclencher explicitement
le traitement d’un point de jonction comme c’est le cas en FAC via la méthode proceed d’un point de
jonction (cf. listing 4.6).
Cette seconde approche nous semble unifiée et promettre une meilleure réutilisation. En effet,
un composant défini et mis sur étagère peut ensuite être utilisé pour construire la partie de base ou
la partie transversale d’une application. Cela n’est pas forcément possible avec les approches asymé-
triques. En AspectJ, si l’on définit un aspect de journalisation par exemple, il ne peut être utilisé de
façon standard puisque ce n’est pas une classe. En S CL, nous avons choisi d’adopter une approche
symétrique et donc de représenter les concepts de la PPA à l’aide des concepts de l’approche à com-
posants.
Choix 23 Les services des composants peuvent être utilisés comme préoccupation transversale
( advice).
Le code transversal peut être encapsulé dans un composant. Par exemple, on peut construire un
composant L OGGER fournissant le service log. Cela permet d’assurer que le code transversal pour une
application donnée puisse être utilisé comme du code de base pour une autre application. Séparer
la définition du code transversal de son utilisation avec une application donnée, en ne spécifiant pas
les points de coupe notamment, permet d’augmenter ses possibilités de réutilisation [Lieberherr et
al., 1999]. Toutefois, les appels à du code transversal (le service log par exemple) ne peuvent être
indépendants du code de l’application puisqu’ils sont par définition disséminés dans le code des
composants métiers. Cette problématique est l’objet de la section suivante.
Fractal-AOP et FAC représentent deux approches différentes en ce qui concerne le tissage. Fractal-
AOP utilise la liaison « standard » (entre une interface requise et une interface fournie) pour lier un
composant à un composant de tissage, lui-même lié à un composant d’advice. Cela suppose de do-
ter tous les composants métiers d’une interface cliente, nommée cEC en Fractal-AOP, définissant les
points de jonctions et permettant sa liaison avec les composants de tissage. FAC n’utilise pas la liaison
standard et a introduit la liaison d’aspect pour lier un composant métier à un composant d’aspect.
Toutefois, ces liaisons d’aspects sont aussi établies entre des interfaces spécifiques des composants
métiers (dits aspectisables en FAC) et des composants d’aspects.
En S CL, nous avons adopté une approche similaire à celle de FAC en introduisant de nouveaux
types de liaison de ports. Cela permet d’une part de bien distinguer les liaisons de type requis/four-
nis de celles relevant des aspects et d’autre part de ne pas introduire de port particulier pour mettre
en place ces liaisons afin de rendre tous les composants potentiellement aspectisables sans effort du
programmeur. La figure 4.5 présente un exemple d’intégration d’aspects dans une application en S CL.
Cet exemple reprend l’architecture à deux composants utilisée précédemment dans la figure 3.12 et
ajoute la fonctionnalité transversale de journalisation en utilisant un composant F ILE L OGGER stan-
dard, un connecteur standard et une nouvelle sorte de liaison de ports de type bSI (before service
invocation). Lorsqu’une invocation de service est reçue par l’un des composants pm ou rng via leurs
ports fournis liés par une liaison de type bSI, cette invocation est d’abord transmise via cette liaison
avant d’être traitée. Le connecteur adHocConnector reçoit alors cette invocation qui la traite norma-
lement et exécute son code glue dans cet exemple.
4.3. Séparer les préoccupations en S CL 131
Generator :BinaryConnector
Randomizer Generator
Checker
I>>
<<bSI>>
bS
<<
<<bSI>>
:AdHocConnector l : FileLogger
Target Logger
glue incoming {
Target.log( "before " + si.port().name() + "." + si. selector() )
}
Incoming
Les éléments permettant de faire de la PPA en S CL sont : de nouveaux types de liaison de ports
et des connecteurs spécifiques facilitant la programmation. Ces nouveaux types de liaison, que l’on
nomme liaisons d’aspects dans la suite, sont :
– before/after service invocation (bSI/aSI), il s’agit des points de jonction avant ou après la récep-
tion (resp. émission) d’une invocation de service pour port fourni (resp. requis),
– before/after connection (bCon/aCon),
– before/after disconnection (bDiscon/aDiscon).
Le listing 4.9 montre comment établir les liaisons représentées sur la figure 4.5. Similairement aux
liaisons normales, de nouvelles constructions syntaxique telles que bindbsi <port>+ to <port> sont
introduites. En plus du sucre syntaxique (bind . . .to . . .glue), il est possible de définir des connecteurs
qui facilitent l’utilisation de ces liaisons.
pm := new PasswordManager
rng := new RandomNumberGenerator
l := new FileLogger
Dans le code d’un composant, il est possible d’utiliser des liaisons d’aspects sur les ports internes
comme l’illustre le listing 4.10. Cela n’est absolument en contradiction avec le modèle boîte noire
puisque les ports internes sont justement accessibles uniquement depuis l’implémentation de leur
composant.
descriptor CDPasswordManager {
providedport Generator
def init {
# ...
bindbsi self to (new FileLogger).Logger glue {
Target.log("internal CDPassword call of " + si.selector )
}
}
# ...
}
Les ports des connecteurs peuvent eux aussi être liés via des liaisons d’aspects puisque ce sont
des composants. Tisser des aspects directement sur les ports des connecteurs ou des composants est
tout à fait similaire.
Le tissage repose donc sur les liaisons d’aspects en S CL. Ainsi, il est possible de tisser dynamique-
ment des aspects en établissant des liaisons mais aussi d’en enlever en supprimant les liaisons.
Enfin, un conflit de tissage se produit lorsqu’un port possède deux liaisons d’aspect du même
type. De même qu’en FAC, les conflits peuvent être résolus par l’architecte en spécifiant un ordre
d’exécution entre les liaisons d’aspects en conflit. Le listing 4.11 montre un exemple de résolution
(mot-clef priority) en spécifiant le niveau de priorité (un entier) pour chaque liaison du même type
pour un port. Attention, deux liaisons du même type pour un même port doivent posséder une prio-
rité qui ne peuvent être identiques.
pm := new PasswordManager
lf := new FileLogger
lbd := new BDLogger
L ISTING 4.11 : Résolution de conflits explicite entre des liaisons d’aspects via des niveaux de priorité
4.4. Les propriétés observables des composants 133
Afin d’éviter la résolution de conflits explicite systématiquement, nous avons introduit une règle
de priorité implicite en S CL qui est que l’ordre entre les liaisons est le même que celui de la mise en
place des liaisons. Ainsi, dans l’exemple du listing 4.11, préciser les priorités est inutile puisque la
première liaison établie est bien celle qui est prioritaire.
4.3.4 Critique
S CL propose donc un modèle de points de jonction de type boîte noire comme la plupart des mo-
dèles industriels et FAC afin de garantir le découplage. S CL intègre aussi des liaisons d’aspects spéci-
fiques pour les points de jonction qu’il supporte. Ces liaisons explicitent le couplage entre le code de
base et le code transversal. Ensuite, les connecteurs de S CL et la souplesse apportée par le code glue
permettent de séparer les préoccupations au mieux. La propriété de non-anticipation (obliviouness)
est respectée puisque les liaisons d’aspects « s’attachent » directement sur les ports des composants.
Un programmeur de composant ne doit pas explicitement rendre son composant aspectisable en le
dotant d’interfaces particulières comme c’est le cas par exemple en FAC ou Fractal-AOP. Du point
de vue de la dynamicité, le tissage est effectué dynamiquement et est réversible en S CL puisqu’il est
complètement basé sur la connexion comme en Fractal-AOP et FAC. Du point de vue de l’intégration
des aspects, FuseJ propose une solution comparable à celle de S CL bien que la gestion des conflits ne
soit pas traitée. Par contre, du point de vue des composants, FuseJ est bien moins unifié que S CL car
il utilise une classe Java standard pour implémenter un composant négligeant ainsi, dans le code, la
notion de « port » par exemple. Le principal désavantage de S CL est actuellement le manque de lan-
gage de coupes. En effet, établir des liaisons d’aspect est bien plus fastidieux qu’utiliser un langage de
coupes déclaratif qui permet au programmeur de décrire succinctement les coupes. Toutefois, il est
possible de construire des connecteurs en S CL facilitant la mise en place des liaisons d’aspects en se
basant sur des descriptions à base d’expression régulières.
Dans cette section, nous présentons une problématique générale des approches à composants
concernant la possibilité d’établir, de façon non-anticipée, des connexions entre les composants ba-
sées sur les changements d’état de leurs propriétés. Cette problématique relève de la séparation des
préoccupations car l’établissement de telles connexions entre les composants nécessite normale-
ment que leur programmeur ait écrit du code spécifique à cet effet. Nous pensons que le code mé-
tier des composants ne doit pas contenir de code transversal permettant l’établissement de telles
connexions. Il existe peu de solutions à ce problème dont la plus notable est certainement Super-
Glue [McDirmid et Hsieh, 2006] que nous présentons brièvement avant de détailler celle que nous
avons intégrée à S CL.
4.4.1 Problématique
comme c’est le cas en CCM, Fractal ou encore ArchJava. Dans ces conditions, il est difficile d’établir
des connexions basées sur les changements d’état des composants. Pourtant, cela est particulière-
ment utile notamment dans le cas des interactions entre les entités modèles et les entités vues d’une
application architecturée selon le modèle MVC [Krasner et Pope, 1988].
De nombreuses techniques existent pour déclencher des traitements en réaction à des change-
ments d’état comme le schéma de conception Observateur [Gamma et al., 1995] ou encore les atta-
chements procéduraux [Minsky, 1975] qui permettent d’attacher des procédures à l’accès d’un attri-
but afin qu’elles soient automatiquement exécutées lors de chaque accès à cet attribut. Plus géné-
ralement, cette problématique relève du protocole de communication publish/subscribe [Eugster et
al., 2003] qui est particulièrement utile et permet un bon découplage entre les entités comme énoncé
dans [Garlan et Shaw, 1993] : “The main invariant in this style is that announcers of events do not know
which components will be affected by those events". Toutefois, la mise en place de ce protocole de com-
munication doit être effectuée par le programmeur des composants ce qui contredit le principe de
non-anticipation à deux niveaux :
Côté émetteur. Le programmeur d’un composant qui notifie ses changements d’état, doit explicite-
ment écrire du code relatif à la gestion des écouteurs (ajout/retrait) et leur notification. C’est
le cas dans le modèle Javabeans (cf. listing 2.4) mais aussi dans la plupart des autres modèles
comme CCM où il faut équiper le composant d’une source d’événements et lever les événe-
ments explicitement dans le code du composant ou en Fractal où il faut utiliser une interface
cliente optionnelle à travers laquelle on invoque les services de notification.
Côté récepteur. Il peut être nécessaire qu’un composant soit programmé de façon particulière pour
recevoir des notifications. Par exemple, en CCM, le composant doit être équipé d’un puits
d’événements dont le type est compatible avec ceux à recevoir.
Dans la plupart des cas, l’utilisation d’un adapteur [Gamma et al., 1995] permet de s’affranchir
des contraintes du côté récepteur alors que cela est impossible pour celles du côté émetteur.
En résumé, un langage à composants doit offrir selon nous :
– la possibilité de déclarer l’état exporté par un composant lors de sa définition, sans compro-
mettre l’encapsulation c’est-à-dire révéler la façon dont est stocké cet état en interne,
– la possibilité de connecter des composants en se basant sur les changements de valeurs de leurs
propriétés, sans qu’ils aient été programmés spécifiquement pour supporter l’abonnement et
la notification (respect de la non-anticipation).
5 http://www.borland.com/fr/products/delphi/index.html
4.4. Les propriétés observables des composants 135
class TimeStamp
{
// Nombre de seconde depuis le 1er janvier 1970 à 00 : 00 : 00 GMT (Norme)
private double s;
class Test
{
public static void Main() {
TimeStamp ts = new TimeStamp();
ts.Time = 1234567.89; // Modification de la valeur de la propriété Time
Console.WriteLine(ts.Year); // Accès à la valeur de la propriété Year
}
}
L’idée de doter les propriétés de comportement de notification est issue du modèle Javabeans.
Dans les langages actuels les propriétés ne notifient pas automatiquement leurs changements d’état
et cela doit être mis en place par le programmeur ce qui n’est pas une solution envisageable du point
de vue de la non-anticipation. Ce code de notification étant fastidieux et récurrent, des constructions
syntaxiques simplifient son écriture comme c’est le cas en C# [Hejlsberg et al., 2003] avec les delegates,
mais cela n’est toujours pas satisfaisant.
Dans le domaine des composants, le langage de programmation SuperGlue [McDirmid et Hsieh,
2006] adresse ce problème. En SuperGlue, un composant est soit une instance d’un atom (implé-
menté en Java), soit d’un compound (implémenté en SuperGlue). Dans tous les cas, un composant
est constitué de signals exportés ou importés. Un signal représente une valeur typée variant dans
le temps (time-varying values). Le listing 4.13 montre la déclaration de deux atoms, leur instancia-
tion et le code de deux connexions. La connexion est réalisée via l’opérateur d’égalité. La ligne 13
de cet exemple, signifie que lors de toute modification de valeur du signal temperature, la valeur
du signal text sera automatiquement mise à jour. La deuxième connexion présentée dans ce listing
(cf. ligne 16) utilise la syntaxe classique d’un if mais la sémantique est différente. Les conditions sont
des gardes et dès que l’une d’elles est vérifiée (à tout instant de l’exécution du programme), l’action
correspondante est exécutée.
1 atom Thermometer {
export temperature : int;
3 }
5 atom Label {
import text : String;
7 import color : Color;
}
9
let model = new Thermometer;
11 let view = new Label;
Ce rapide survol de SuperGlue permet de constater que ce langage est vraiment spécialisé pour
réaliser des connexions basées sur les changements d’état des propriétés. D’ailleurs, la connexion de
composants repose uniquement sur la liaison de signals. Le prototype actuel de SuperGlue utilise le
schéma de conception Observateur [Gamma et al., 1995] pour détecter les changements d’état des
signaux.
S CL présente la possibilité d’établir des connexions de type requis/fourni et de type aspects. Dans
4.4. Les propriétés observables des composants 137
la section suivante, nous présentons une extension de S CL afin de réaliser aussi des connexions ba-
sées sur les changements d’état sans qu’elles aient été prévues par les programmeurs des compo-
sants.
Une propriété
c : ChatClient ta : TextArea
ChatText
Chat Display
getChatText() bc : BinaryConnector
setChatText(t)
glue incoming {
if( si.selector == :nac)
Target.displayText(si.argument(2))
}
displayText(t)
nac(oldV,newV)
F IG . 4.6 : Utilisation des propriétés pour établir des connexions basées sur les changements d’état
Le listing 4.14 montre la déclaration du composant C HAT C LIENT et notamment celle de la propriété
ChatText. La déclaration des propriétés s’effectue via le mot-clef property dans notre langage. Les
accesseurs de la propriété sont des services définis par le programmeur du composant6 . Dans cet
exemple, les accesseurs de la propriété ChatText accèdent au port interne ct.
6 Cette tâche est souvent facilitée par du sucre syntaxique dans les langages lorsque la propriété n’accède qu’à un seul
attribut.
138 Chap 4. Séparation des préoccupations en S CL
descriptor ChatClient {
providedport Chat, IChat
intern requiredport ct
intern requiredport nickname
intern requiredport socket
property ChatText {
# definir le port d’accès permet de choisir le nom du port et ceux des accesseurs
# une convention pourrait être utilisée pour faciliter la tâche du programmeur
accessport AccessCT, {getChatText; setChattext(v)}
}
def init {
bind ct to new Text
# ...
}
Dans le code du service receive, un service est invoqué via ct et l’exécution de ce service peut
modifier le sous-composant de telle sorte que la valeur de la propriété ChatText sera elle aussi mo-
difiée. L’interprète de S CL doit détecter les changements de valeur des propriétés pour effectuer les
notifications. La détection de ces changements peut être mise en place via les liaisons d’aspects pré-
sentées dans la section précédente. La figure 4.7 montre une possible organisation interne du com-
posant ChatText permettant d’effectuer automatiquement les notifications de changement d’état.
Bien évidemment la mise en place de cette organisation est effectuée par le compilateur ou l’éva-
luateur puisque la notification ne doit pas reposer sur le programmeur. De plus, si un programmeur
souhaite effectuer lui-même une notification, il peut le faire directement et simplement en invoquant
le service nac à travers le port de notification de la propriété.
c : ChatClient
self
ct :Text
I>>
<<bS
<< getChatText()
I>>
bS setChatText(t)
I>>
<<bS
Chat
:AdHocConnector
ChatText
<<aSI>>
glue Save {
# sauver la valeur de la ChatText
# si l'invocation si est de l'une de ces 3 trois formes :
# ct.* ou self.setChatText ou ChatText.setChatText
oldV := Access.getChatText
Save } PropertyAccess
F IG . 4.7 : Une organisation interne détectant les changements de valeurs de propriété qui peut être
générée par un compilateur ou évaluateur
Critique. Contrairement à S CL, SuperGlue ne supporte que les connexions basées sur les change-
ments d’état des composants. L’ajout de propriétés observables en S CL permet d’offrir une possibi-
lité d’assemblage supplémentaire à l’architecte, augmentant ainsi les possibilités de réutilisation des
composants, sans déroger au principe de non-anticipation. Toutefois, le programmeur de compo-
sants peut toujours fournir directement des accesseurs, sans déclarer de propriété. Cela constitue
actuellement une limitation de cette extension de S CL qu’il faudrait lever en imposant la déclaration
systématique de propriétés pour exporter de l’état.
140 Chap 4. Séparation des préoccupations en S CL
4.5 Conclusion
CHAPITRE
Prototypes de S CL
Make it work.
Make it work right.
Make it work right and fast.
Préambule
Ce chapitre présente deux prototypes de S CL. Le premier est implémenté en Smalltalk ou plus exac-
tement en Squeak qui est une implémentation de Smalltalk. Nous présentons d’abord ce premier proto-
type, qui est le plus abouti, à travers nos choix de conception et des exemples de code. Nous présentons
aussi une ébauche d’environnement graphique pour développer en S CL. Ensuite, le deuxième proto-
type, écrit en Ruby, est rapidement présenté avant de conclure ce chapitre.
141
142 Chap 5. Prototypes de S CL
U
N premier prototype de S CL a été réalisé en Squeak [Ingalls et al., 1997]. Squeak1 est une implé-
mentation libre 2 , moderne et portable du langage et de l’environnement Smalltalk [Goldberg
et Robson, 1989]. Smalltalk est un langage à objets réflexif et dynamiquement typé. Les langages dy-
namiquement typés offrent une flexibilité et des qualités encore reconnues [Nierstrasz et al., 2005].
Les principales caractéristiques du modèle objet de Smalltalk sont l’héritage simple et la notion de
méta-classe implicite c’est-à-dire qu’une métaclasse est créée automatiquement pour chaque classe
du système sans que le programmeur n’ait à la définir. Du fait de la réflexivité, Smalltalk est un lan-
gage de programmation uniforme (« tout est objet ») et ouvert ce qui facilite l’écriture d’extensions.
Squeak [Ingalls et al., 1997; Black et al., 2007] est une implémentation de Smalltalk écrite en Smalltalk3
ce qui facilite les extensions de la machine virtuelle Squeak.
Outre les qualités de Smalltalk et de Squeak, nous avons aussi constaté que la majorité des pro-
totypes actuels de langages à composants sont des extensions de Java. Il semble donc intéressant de
proposer une alternative dans un environnement différent afin de mieux distinguer les spécificités
des langages à composants de celles du langage Java.
1 http://www.squeak.org
2 http://www.gnu.org/philosophy/free-sw.html
3 Plus exactement en un sous-ensemble de Smalltalk nommé Slang.
4 http://www.refactory.com/Software/SmaCC/
5.3. Amorçage de l’implémentation 143
« Smalltalk seems much more a notation, that can do nothing but build domain specific
languages. [...] When you create a domain specific language in Smalltalk, your code never
looks different than code provided by the compiler writer himself, it’s one syntax to rule
them all. [...] Smalltalk shares this trait with languages such as Lisp and Scheme, truly
growable languages that put you, the programmer, in charge of what language you want,
not some compiler writer who might take six more years to add some feature you just got
to have now. When you need a new language feature in Smalltalk, you simply add it. [...]
Having a succinct notation for the delayed evaluation of code, and allowing that code to
be placed into variables and passed around, is all one needs to build virtually any domain
specific language they can dream up. » [Leon, 2006]
Un autre avantage à utiliser directement les constructions de Smalltalk est l’accès direct aux outils
de développement Smalltalk (browser, debugger, etc.) avec du code écrit dans le nouveau langage.
Nous pensons que cette implémentation est « idéale » dans le sens où elle n’intègre que très peu
de mécanismes existants en Smalltalk. En effet, la méta-classe ComponentDescriptor hérite de la
classe Object (et non de la classe Class) ce qui permet d’implémenter uniquement les comporte-
ments propres à S CL sans hériter ceux spécifiques à Smalltalk comme l’héritage entre les classes par
exemple. Toutefois, nous n’avons pas retenu cette implémentation « idéale » car :
– les méta-classes sont implicites en Smalltalk et créées automatiquement. Il n’est donc pas pos-
sible de construire une méta-classe héritant de la classe Object sans utiliser une extension de
144 Chap 5. Prototypes de S CL
La figure 5.3 montre l’architecture générale de l’implémentation de S CL. Cette architecture com-
prend quatre « niveaux », chacun étant constitué d’éléments différents :
1. Le premier niveau est celui des classes de Squeak telles que Object ou Object class.
2. Le second niveau comprend les classes relevant de la mise en œuvre de S CL telles que
Component ou Component class. Dans la suite, nous décrivons plus précisément les classes
de ce niveau et leurs relations.
3. Le troisième niveau est celui des descripteurs de composants. C’est dans ce niveau qu’un pro-
grammeur S CL écrit des descripteurs de composants. Nous verrons dans la suite des exemples
de définition de descripteurs en S CL.
5.5. Le modèle implémenté 145
4. Enfin, le quatrième niveau est celui des instances c’est-à-dire des composants qui peuvent être
assemblés. Le composant SclBuilder est important comme nous le verrons dans la suite.
3.
Descripteurs de PasswordManager Connector BinaryConnector DefaultDescriptorBuilder
composants
en Scl
Le deuxième niveau de cette architecture est détaillé par le schéma UML présenté à la figure 5.4.
Cette figure présente le modèle de S CL où la classe Component (qui représente les compsants) est
centrale. Cette classe est une instance de la méta-classe Component class qui introduit un dic-
tionnaire de ports de façon analogue à la classe Behavior, dont elle hérite (cf. figure 5.2), qui in-
troduit un dictionnaire de méthodes. Un composant est donc constitué de ports (Port) apparte-
nant éventuellement à une collection de ports (MultiPort). Un port est décrit par une interface
(Interface) qui comprend un ensemble de signatures de service (Service). On distingue les ports
requis (RequiredPort) et les ports fournis (ProvidedPort) ainsi que les deux ports particuliers que
sont self (SelfPort) et default (DefaultPort). Un port fourni peut être associé à un service glue
146 Chap 5. Prototypes de S CL
(GlueService) qui est un service particulier du composant. Les liaisons sont représentées par des
associations au niveau des ports :
– les liaisons standards permettent de lier un RequiredPort à un ProvidedPort,
– les liaisons de délégation permettent de lier deux ports de même nature.
+delegate
+delegate
{subset delegate}
{subset delegate}
1 1
RequiredPort +boundedPort ProvidedPort * GlueService
* * 0..1 1 +glue 0..1
SelfPort DefaultPort
Le listing 5.1 montre un exemple de définition d’un descripteur de composant en S CL (il s’agit de
celui décrit dans le listing 3.1). Dans ce code, le composant SclBuilder (cf. figure 5.3) est utilisé. Ce
composant est un singleton [Gamma et al., 1995] permettant la construction de descripteurs de com-
posant tout masquant aux programmeurs les détails d’implémentation. La déclaration de chaque
port comprend :
– son nom qui est un symbole en Smalltalk et commence donc par le caractère #,
– son interface qui est une collection5 de symboles correspondants à des sélecteurs.
Ensuite, les codes source (uniquement les points clés) des services sont montrés. Le premier ser-
vice (getRandomCharacterWithLetters:) est interne et son code contient une invocation via le port
requis Randomizer du composant. Nous reviendrons dans la suite sur la syntaxe de l’invocation de
service qui est la même que celle de l’envoi de message en Smalltalk (le caractère espace). Les codes
source des autres services montrent plusieurs exemples d’invocation dont certaines à travers le port
Self. Finalement, ce descripteur est instancié via la primitive newC. Nous verrons dans la section sui-
vante (cf. section 5.6) pourquoi cette primitive est nommée newC et non new. Rappelons toutefois que
l’instanciation d’un descripteur retourne une référence sur le port nommé Default du composant
nouvellement créé (cf. choix 8 page 86).
5 Squeak a introduit la syntaxe avec des accolades pour permettre l’écriture de tableaux de façon littérale.
5.6. L’intégration des objets de base 147
PasswordManager>>getRandomCharacterWithLetters: b
"retourne un chiffre choisi aléatoirement"
"b indique si le caractère peut être une lettre minuscule"
"..."
i:= Randomizer getRandomNumber. "invocation via le port Randomizer"
"..."
PasswordManager>>generateADigitsOnlyPwd: size
"Génère un mot de passe de taille size avec des chiffres uniquement"
"..."
i := Self getRandomCharacterWithLetters: false. "invocation interne via le port Self"
"..."
PasswordManager>>isValid: aPwd
"retourne un booléen indiquant si aPwd est valide en vérifiant sa difficulté par exemple"
"..."
pm := PasswordManager newC.
– newC (contraction de newComponent) dans la classe Object class qui permet d’instancier un
composant à partir d’une classe et retourne une référence vers le port Default de ce compo-
sant,
– defaultPort qui permet d’obtenir un objet représentant le port par défaut de n’importe quel
objet Smalltalk. L’interface de ce port est initialisée avec l’ensemble des signatures (un sélecteur
contient l’arité de la méthode en Smalltalk) de messages auxquels cet objet peut répondre.
Dans l’exemple suivant, nous montrons des utilisations de ces méthodes pour intégrer les objets
de base Smalltalk sous la forme de composants en S CL :
Dans le cas des littéraux comme les entiers ou les chaînes de caractères, le programmeur S CL
doit stocker la référence vers leur port par défaut en utilisant la méthode defaultPort. En effet, les
littéraux ne profitent pas directement de la méthode newC définie dans la classe Object class car ils
ne sont jamais vraiment instanciés mais traités spécifiquement par la machine virtuelle Squeak. Afin
d’uniformiser la vision offerte au programmeur S CL, nous avons aussi défini la méthode newC dans la
classe Object de la façon suivante :
Object>>newC
^self defaultPort
En résumé, le programmeur S CL doit toujours utiliser la primitive6 newC pour instancier des des-
cripteurs et des classes ou pour obtenir une référence sur le port par défaut d’un objet :
6 Nous utilisons le terme « primitive » et non celui de « méthode » pour mettre l’accent sur le fait que le code ce méca-
nisme doit être considéré comme « opaque » par le programmeur S CL puisqu’il relève du second niveau dans l’architecture
de notre prototype.
5.7. L’invocation de service 149
pm := PasswordManager newC.
c := OrderedCollection newC.
c size
entier := 28 newC.
entier odd
Cette syntaxe est rendue possible par la définition de la méthode suivante dans la classe Port :
7 Il s’agit du mécanisme de base en Smalltalk lorsqu’un objet ne possède pas de méthode correspondant au sélecteur
L ISTING 5.2 : Méthodes de la classe Port permettant le traitement des invocations de services
5.8. Les liaisons et les connecteurs 151
L ISTING 5.3 : Méthodes permettant le traitement des invocations de service dans les classes
RequiredPort et ProvidedPort
Toutefois, cette syntaxe est assez lourde à l’utilisation et c’est pourquoi nous préférons celle de
l’envoi de message même si :
– elle prête à confusion avec l’envoi de message lors de la lecture du code au premier abord,
– elle est sujette à des conflits. Par exemple, si le sélecteur d’une invocation de service corres-
pond à un sélecteur d’une méthode du port receveur, alors cette méthode est exécutée alors
qu’il faudrait réaliser une invocation de service à destination d’un composant. Pour limiter cet
inconvénient, les noms des méthodes dans la classe des ports pourraient être préfixés.
Comme nous l’avons vu ci-dessus, les liaisons sont mises en place au niveau des ports. En fonc-
tion du type de la liaison et de la nature du port, les invocations de services sont traitées différem-
ment. La primitive bindTo: permet de mettre en place des liaisons de délégation et des liaisons stan-
dards (cf. figure 5.4). De façon analogue, il existe des primitives spécifiques pour les liaisons d’as-
pects : bindBSITo: et bindASITo:.
Le listing 5.5 présente des utilisations de connecteurs. Les connecteurs sont paramétrables par
152 Chap 5. Prototypes de S CL
pm := PasswordManager newC.
rng := RandomNumberGenerator newC.
du code glue qui est représenté par un bloc en Smalltalk (délimités syntaxiquement par des crochets).
Un bloc réifie une fermeture lexicale.
Il est possible de définir ses propres connecteurs en S CL. Cela nécessite d’écrire un descripteur et
de prévoir son paramétrage via des blocs.
Un composant peut déclarer des propriétés observables en S CL (cf. section 4.4.3). Le listing 5.6
montre la déclaration du composant ChatClient que nous avions présentée dans le listing 4.14. Ce
composant est doté d’une propriété nommée ChatText.
ChatClient>>init
ct bindTo: Text newC
"..."
ChatClient>>getChatText
^ ct getText
ChatClient>>setChattext: v
ct setText: v
Le listing 5.7 montre le code permettant d’établir une connexion, entre un C HAT C LIENT et un C HAT-
C LIENT G UI, basée sur les changements d’état de la propriété ChatText (cf. figure 4.6).
5.9. Les propriétés 153
"Supposons que les variables pm, rng, c1..c5 contiennent des références vers des ports"
BinaryConnector newC
source: (client notifyPort: #ChatText)
target: (clientGui port: #Display)
glue: [ :s :t :si |
(si selector == #nac:value:oldValue:) ifTrue: [
t displayText: (si arguments at: 2)
]
].
L ISTING 5.7 : Mise en place d’une connexion basée sur les changements de valeur d’une propriété en
S CL
La figure 5.5 présente une capture d’écran d’un outil de développement visuel permettant l’as-
semblage des composants S CL. Ce prototype d’outil permet actuellement à un architecte de « prendre
à la souris » des composants dans une bibliothèque et de les « poser » dans la palette de construction
des assemblages. Cette action a pour effet de créer une instance du descripteur choisi dans la biblio-
thèque. La liste des instances, c’est-à-dire des composants présents dans l’architecture, est présentée
en dessous de la bibliothèque. En suite, il est possible d’utiliser l’outil de liaison (le bouton avec la
flèche) pour lier un port requis avec un port fourni. Une fois la liaison établie et réussie (contrainte
de compatibilité des interfaces), il est possible d’entrer du code S CL dans la zone de saisie en bas
à gauche pour invoquer les services des composants. Les résultats peuvent être visualisés dans la
fenêtre en bas à droite qui est la sortie standard. Les liaisons peuvent être enlevées en supprimant
les flèches entre les ports. Cet environnement d’assemblage visuel est un prototype simple d’outil
nécessaires à la compréhension et à la mise en pratique du développement par composants. Dans
l’annexe B, nous présentons brièvement les environnements de développement visuels adaptés à
l’approche à composants que nous avons découvert.
5.11. Le cœur d’un prototype en Ruby 155
Composant
Palette de construction d'un assemblage Port requis Interface Liaison Port fourni
Bibliothèque
Composants
F IG . 5.5 : Capture d’écran d’un prototype d’outil visuel pour l’assemblage de composants S CL
Dans cette section, nous présentons brièvement le cœur d’un prototype de S CL écrit avec le lan-
gage à objets Ruby [Anantharam, 2001].
Pourquoi Ruby ? Contrairement à Smalltalk, Ruby utilise une syntaxe mieux connue actuellement.
C’est donc uniquement la barrière de la syntaxe qui nous a incité à développer un autre prototype
que celui en Smalltalk. Notre choix s’est porté sur Ruby car :
– sa syntaxe est simple et relativement proche de tous les langages actuels tels que Java ou C#,
– il est inspiré de Smalltalk et possède donc des caractéristiques similaires : langage à objets ré-
flexif, réification des fermetures, héritage simple, etc,
– il est très bien adapté à la création de DSL.
Implémentation. Nous avons fait exactement les mêmes choix de conception que ceux présentés
dans la section 5.2 concernant le prototype en Smalltalk. De ce fait, l’amorçage, l’architecture générale
156 Chap 5. Prototypes de S CL
ou encore le modèle implémenté sont très similaires. Le listing 5.8 présente un exemple de code S CL
écrit avec ce protoype en Ruby. Nous pouvons constater que la syntaxe de S CL est très proche de celles
que nous avons utilisée dans les chapitres 3 et 4.
componentDescriptor :CDPasswordManager do
providedPort :in1, [:generatePwd, :generateADigitsOnlyPwd]
providedPort :in2, [:isValid]
requiredPort :out, [:getRandomNumber]
def generatePwd
# ...
size=out.getRandomNumer(taille)
for i in 1..size
j=out.getRandomNumer(25)
# ...
end
pwd
end
end
componentDescriptor :CDRandomNumberGenerator do
providedPort :in, [:rand]
componentDescriptor :CDMyAdHocConnector do
providedPort :source1, IRandomizer
requiredPort :target1
# le service _glue1 est défini comme service glue associé à un port fourni source1
glue :source1, :_glue1
def _glue1( sel, *args )
((target1.rand*args[0])+1).to_i
end
end
# new retourne une référence sur le port par défaut du composant créé
pm = CDPasswordManager.new
rng = CDRandomNumberGenerator.new
c = CDMyAdHocConnector.new
5.12 Conclusion
Dans ce chapitre, nous avons présenté deux prototypes de S CL écrits respectivement en Smalltalk
et en Ruby. L’implémentation du prototype écrit en Smalltalk est bien plus détaillée car c’est actuel-
lement la plus avancée des deux. Nous avons aussi présenté les arguments qui nous ont poussés
à choisir le langage Smalltlak et nos choix d’implémentation dont celui de ne pas faire de parseur.
Concernant ce prototype écrit en Smalltalk, nous avons :
– abordé le problème d’amorçage du prototype qui impose que la classe représentant un des-
cripteur de composant hérite de la classe Class,
– présenté l’architecture en couches du prototype et notre volonté de masquer aux utilisateurs
de S CL les couches relevant de l’implémentation,
– décrit le modèle UML implémenté,
– intégré les objets de base et les composants en proposant la primitive newC,
– détaillé l’implémentation de l’invocation de service qui utilise la même syntaxe que celle de
l’envoie de message mais dont le fonctionnement est conforme à celui définit dans le cha-
pitre 3,
– illustré l’assemblage de composants à l’aide de liaisons, de connecteurs et de code glue à travers
des exemples,
– défini des propriétés de composants afin d’établir des connexions basées sur leurs change-
ments d’état,
– montré, pour finir, un outil visuel pour assembler des composants S CL.
Ensuite, nous avons présenté le prototype écrit en Ruby en commençant par les raisons qui nous
ont conduites à l’écrire et en terminant par un exemple de code.
Le point d’amélioration de ces prototypes est très certainement l’efficacité. En effet, par nos choix
nous avons clairement privilégié l’évolutivité à l’efficacité. Il est difficile de quantifier l’efficacité des
prototypes actuels mais l’implémentation que nous avons choisie pour l’invocation de service (délé-
gation explicite entre les objets représentant les ports) laisse supposer des performances médiocres.
6
CHAPITRE
Bilan et Perspectives
Préambule
Ce chapitre dresse le bilan du travail réalisé au cours de cette thèse et présente cinq perspectives qui
nous intéressent suite à ce travail. La première concerne la formalisation de S CL sous la forme d’une
sémantique opérationnelle. La seconde propose l’étude d’une version réflexive de S CL où les descripteurs
seraient des composants assemblables. La troisième, plus pragmatique, soulève le besoin de concevoir
des outils de développements adaptés à la PPC. Dans la quatrième perspective, nous présentons notre
vision à long terme de ce travail qui est d’aller vers une plateforme d’expérimentation extensible des
concepts et mécanismes relatifs à la PPC. Enfin, dans la cinquième et dernière perspective que nous
présentons, nous détaillons notre vision en ce qui concerne la vérification des assemblages actuellement
limitée en S CL.
159
160 Chap 6. Bilan et Perspectives
C
ETTE thèse s’inscrit dans le domaine de recherche du développement par composants, actuelle-
ment très actif à cause de la difficulté de concevoir, de maintenir et de faire évoluer des appli-
cations de plus en plus complexes. En effet, les approches à composants promettent une meilleure
modularisation des logiciels de grande taille facilitant ainsi leur conception, leur maintenance et leur
évolution. Malgré de nombreuses propositions (technologies, modèles) à composants, on utilise en-
core aujourd’hui principalement des langages de programmation à objets provoquant ainsi une rup-
ture entre les modèles architecturaux et le code source.
Dans ce mémoire, nous avons abordé cette problématique en étudiant les principales approches
à composants (cf. chapitre 2) ce qui nous a permis d’isoler les deux notions qui nous semblent fonda-
mentales pour la programmation par composants (PPC) : le découplage et la non-anticipation.
Le découplage consiste à créer les composants indépendamment de leurs contextes de définition
ou d’utilisation. En effet, les composants sont créés dans un contexte donné et généralement conçus
pour un contexte donné c’est-à-dire une application particulière alors qu’ils devraient être conçus
pour être réutilisés (design for reuse). Afin d’augmenter leurs possibilités de réutilisation, ils doivent
tout de même pouvoir être adaptés à différents contextes d’utilisation. Pour cela, le découplage im-
pose une encapsulation forte des composants en les dotant de ports et/ou d’interface qui spécifient
toutes les interactions entre un composant et son environnement.
La non-anticipation — rarement mise en avant dans les travaux sur les composants — nous
semble primordiale pour le développement par composants. Par non-anticipation, nous désignons
le fait que les programmeurs de composants ne doivent en aucun cas écrire de code spécifique et
non-métier pour permettre l’assemblage ou l’utilisation d’un composant d’une façon particulière et
donc prévue lors de sa création. En effet, dans un tel contexte, les possibilités de réutilisation des
composants seraient directement proportionnelles à la capacité de leur programmeur à prévoir un
maximum d’utilisations potentielles de leur composants.
Le manque d’un langage de programmation simple et vraiment dédié à la programmation par
composants qui respecte ces deux notions clés nous a conduit à proposer dans le chapitre 3 le langage
à composants S CL (Simple Component Language). Avec S CL, notre objectif est de faciliter l’écriture
d’applications par assemblage de composants logiciels en offrant au programmeur des abstractions
et des mécanismes de haut niveau pour ce mode de développement. La spécification de ce langage
a été faite en adoptant une démarche constructive, c’est-à-dire que nous avons identifié un à un les
besoins de ce mode de développement et intégré les concepts et/ou mécanismes minimaux couvrant
ce besoin à partir de l’étude des propositions existantes. Cette démarche nous permet de penser que
S CL synthétise les concepts et mécanismes fondamentaux des approches existantes. Toutefois, nous
avons aussi traité des problématiques cruciales qui pour certaines ne sont pas (ou peu) abordées par
les approches existantes comme :
– l’intégration des types de bases (entiers, chaînes de caractères, etc.) qui sont considérés comme
des composants en S CL ;
– la nécessité d’effectuer des auto-références qui nous a conduit à introduire le port interne self ;
– le passage d’arguments, souvent problématique pour le contrôle de l’intégrité des communi-
cations, qui repose sur la connexion en S CL ;
– la nécessité d’adapter les composants lors de leur assemblage à cause des incompatibilités
structurelles ce qui nous a amené à introduire la notion de connecteur et de code glue.
Bilan et Perspectives 161
Par ailleurs, une extension de S CL a été proposée dans le chapitre 4 pour supporter la séparation
des préoccupations en s’inspirant des techniques issues de la programmation par aspects. En effet,
l’approche à composants — comme l’approche à objets — ne permet pas de bien modulariser les pré-
occupations transversales. En s’inspirant de certaines approches mixtes à aspects et à composants
dites symétriques, nous avons introduit en S CL de nouveaux types de liaisons de ports. Toutefois,
contrairement à ces approches, nous avons respecté nos deux principes initiaux de découplage et de
non-anticipation, ce qui permet à un même composant S CL d’être assemblé de façon standard ou de
façon transversale indépendamment de sa définition. De plus, la souplesse du mécanisme d’assem-
blage de composants de S CL grâce aux connecteurs et au code glue permet d’effectuer le tissage via
le mécanisme d’assemblage standard en utilisant ces nouveaux types de liaisons de ports.
Nous avons aussi intégré la notion de propriété observable en S CL afin d’autoriser les connexions
basées sur les changements d’états des propriétés. Dans les approches existantes, les propriétés sont
souvent restreintes à un couple d’accesseurs alors que le modèle Javabeans a largement montré qu’il
est intéressant de doter les propriétés de comportement signalant leurs changements de valeurs.
Cela permet d’établir des connexions basées sur les changements de valeur de propriétés. Dans les
approches existantes (Javabeans y compris), le programmeur d’un composant doit écrire du code
spécifique afin de rendre possible l’établissement de telles connexions. Nous proposons en S CL un
mécanisme permettant au programmeur de s’affranchir de toutes ces lignes de code contredisant la
non-anticipation. Ainsi, un programmeur déclare des propriétés (au même titre qu’il déclarerait des
accesseurs) ce qui permet ensuite d’établir des connexions basées sur les changements de valeurs de
ces propriétés.
Afin de concrétiser S CL, un premier prototype a été développé en Smalltalk (cf. chapitre 5). Ce
prototype a été implémenté en utilisant les capacités réflexives et la souplesse de la syntaxe de Small-
talk. La notion de bloc (fermeture lexicale) a été particulièrement utile pour simplifier la définition
des connecteurs et du code glue. Nous avons aussi présenté une ébauche d’environnement de déve-
loppement graphique. Le cœur d’un deuxième prototype a été écrit en Ruby car il utilise une syntaxe
proche des langages les plus connus comme Java ou C# alors que Smalltalk utilise une syntaxe sou-
vent mal connue.
Les perspectives de ce travail sont multiples tant sur le plan conceptuel qu’opérationnel. Nous
détaillons ici celles qui nous intéressent le plus et témoignent de notre vision de l’avenir pour S CL.
Formalisation. Tout d’abord, nous souhaitons formaliser le cœur de S CL sous la forme d’une sé-
mantique opérationnelle. Nous avons d’ailleurs commencé ce travail avec la collaboration de Guy
Tremblay en utilisant l’outil LETOS [Hartel, 1999]. Le premier résultat de ce travail est la formalisa-
tion de la syntaxe abstraite du langage S CL, c’est-à-dire la définition des principales constructions
syntaxiques (sous forme abstraite) ainsi que les principaux concepts sémantiques de S CL. La pro-
chaine étape pour obtenir une sémantique opérationnelle consiste à décrire la partie dynamique de
S CL qui s’articule essentiellement autour du mécanisme d’invocation de service.
Réflexivité. Nous envisageons aussi la possibilité d’utiliser le même mécanisme d’assemblage aussi
bien au niveau des composants qu’au niveau des descripteurs. Notre volonté d’unifier le niveau des
composants et celui des descripteurs provient du fait que dans certaines approches, l’assemblage est
162 Chap 6. Bilan et Perspectives
effectué au niveau des descripteurs et non au niveau des instances comme en S CL. C’est par exemple
le cas avec les traits [Schärli et al., 2003; Nierstrasz et al., 2006] qui sont essentiellement des groupes
de méthodes (fournies et requises) utilisés pour construire des classes grâce un mécanisme de com-
position. De même, dans le langage Scala [Odersky et Zenger, 2005] les composants sont des classes
et l’assemblage repose sur la composition à base de mixins. Dans ces approches, les classes et les
traits sont des entités différentes et non unifiées n’offrant pas les mêmes possibilités alors qu’elles
sont toutes les deux vouées à être réutilisées. Pour atteindre cet objectif d’unification en S CL, nous
pensons qu’il faut réfléchir à une version réflexive de S CL. En effet, si les descripteurs deviennent
des composants assemblables et instanciables, il serait alors possible d’utiliser le même mécanisme
d’assemblage au niveau des descripteurs et des composants. Cela soulève bien sûr de nombreuses
questions telles que : Un descripteur peut-il être un composant ? Les ports et les services peuvent-ils
être des composants ? Peut-on imaginer construire un descripteur de composants en assemblant des
composants ? Comment traiter la méta-régression ? . . . Il semble possible d’apporter des éléments de
réponses à certaines de ces questions. Par exemple, on peut considérer un service comme un com-
posant où les paramètres sont des ports requis (liés aux arguments lors de l’invocation), l’implémen-
tation est du code glue associé à un port fourni permettant l’invocation du service. Toutefois, cette
perspective nécessite un travail important qui pourra éventuellement impliquer des changements
profonds dans le cœur de S CL.
Environnement de développement. En plus d’un interpréteur, nous souhaitons concevoir des ou-
tils spécifiques pour le développement par composants. Les nouveaux besoins de ce mode de dé-
veloppement comme la mise à disposition dans une bibliothèque, la recherche de composants ou
encore l’assemblage de composants sont de plus en étudiés et des modèles sont proposés. Toutefois,
il existe en pratique peu d’outils pour ces nouvelles tâches et encore moins dans un environnement
intégré. Nous pensons que la mise en pratique de la programmation par composants nécessite des
outils spécifiques. Il existe d’ailleurs actuellement des propositions d’environnements visuels dédiés
à la programmation par assemblage. Nous avons réalisé un premier pas dans ce sens avec l’outil de
développement graphique que nous avons présenté dans le chapitre 5.
Vers une plateforme d’expérimentation extensible. Nous souhaitons que S CL constitue une base
pour comprendre, étudier mais aussi expérimenter les concepts et mécanismes relatifs au développe-
ment par composants. En effet, S CL est bien plus simple que les approches industrielles qui comme
nous l’avons vu sont souvent complexes, difficiles à mettre œuvre et à étendre. Nous pensons aussi
que l’uniformité de S CL en fait un meilleur candidat pour expérimenter l’approche à composants
que d’autres approches telles que Julia ou ArchJava. Fort de ces atouts, nous pensons qu’il serait in-
téressant de disposer pour S CL d’un noyau formalisé et d’un environnement d’expérimentation fa-
cilement extensible. Cela comprendrait la possibilité d’étendre le noyau de S CL mais aussi les outils
de l’environnement de développement afin d’expérimenter facilement les solutions proposées à de
nombreuses problématiques relatives au développement par composants. Comme nous l’avons fait
en définissant des extensions pour les aspects et les propriétés observables, nous souhaiterions pou-
voir facilement définir des extensions opérationnelles avec un minimum d’effort.
Bilan et Perspectives 163
ANNEXE
Programmation par composants avec des
langages de programmation actuels
Les absurdités d’hier sont les vérités d’aujourd’hui et les banalités de demain.
Alessandro M ORANDOTTI.
Préambule
Dans ce chapitre, nous montrons comment utiliser les langages de programmation existants pour
programmer par assemblage de composants. La COP ( Component-oriented programming) peut en
effet, selon nous, être mise en pratique dans la plupart des langages de programmation existants si l’on
respecte certaines règles de programmation. Bien évidemment, suivant le langage choisi et les règles à
respecter, la POC peut être plus ou moins facile en œuvre.
165
166 Annexe A. Programmation par composants avec des langages de programmation actuels
Notre objectif est de montrer dans cette annexe que si le programmeur respecte des règles de
programmation données, il peut faire de la POC dans les langages de programmation actuels. Dans
les sections qui suivent, nous présentons les règles à respecter pour faire de la POC en C et en Java.
Nous avons établit ces règles afin de maximiser le découplage entre les entités logicielles.
A.1 PPC en C
Dans cette section, nous montrons qu’il est possible d’adopter un style de programmation par
composants avec un langage procédural et modulaire comme C. On aurait sans doute pu utiliser
n’importe quel langage de cette famille comme ADA ou Pascal pour faire cette démonstration.
– composant compilé = fichier .o ou fichier .a
– composant = un fichier .c d’implémentation
– propriétés/attributs d’un composant = les variables "static" (portée du fichier)
– services fournis d’un composant = les fonctions définies dans le fichier c
– services requis d’un composant = les signatures "extern" déclarées
/***************
uncompteur.c
Un composant compteur
***************/
#include "compteur_implementation.c"
/* les propri\’et\’es */
static int val = 0;
/***************
compteur.c
Un composant compteur
***************/
/***************
test.c
Un composant Test fournissant un service main
****************/
/* Un service fournit */
int main () {
int a = value();
printf( "%i \n", a);
incr();
a = value();
printf( "%i \n", a);
return 0;
}
La compilation des composants s’effectue avec gcc afin d’obtenir un .o constituant dans notre
exemple la forme de diffusion des composant :
$ gcc -c compteur.c
$ gcc -c test.c
$ gcc -nodefaultlibs -o test uncompteur.o compteur.o test.o libc.a libgcc.a
La dernière ligne assemble ces composants pour qu’ils fonctionnent ensembles. Pour cela, nous
utiliserons le linker de gcc :
Le composant Test requiert trois services dont deux sont fournis par le composant Compteur et
le dernier par la bibliothèque libc.a elle même possédant des dépendances satisfaites par libgcc.a.
L’exécutable obtenu (test) correspond au résultat d’un assemblage de composants logiciels où toutes
les dépendances ont été satisfaites. L’exécution de notre application test construite par assemblage
de composants nous donne :
$ ./test
0
1
Les langages à objets sont actuellement les plus utilisés. Ils peuvent être utilisés pour faire de
la programmation par composants si l’on respecte des schémas de conception et des conventions.
Nous avons choisi d’utiliser le langage Java pour cette étude car il est le langage le plus utilisé par
les chercheurs pour prototyper des langages à composants (ArchJava, ComponentJ, Lagoona, Julia,
JPiccola, ...). L’objectif est de comprendre comment il serait possible de faire de la programmation
par composants en utilisant le langage Java.
– composant compilé = fichier .class ou .jar
168 Annexe A. Programmation par composants avec des langages de programmation actuels
h.setPrinter(p);
h.sayHello();
}
}
Le composant Helloer requiert un Printer i.e requiert un composant sachant afficher. Ainsi
construit, le composant Helloer pourra utiliser n’importe quel composant qui est un Printer.
MonApplication est un exemple d’application qui connecte une instance de ConsolePrinter à une
instance de Helloer. Le Helloer utilisera dans cette application un ConsolePrinter. Pendant l’exécution
d’une application, on peut changer le Printer qu’utilise le Helloer.
B
ANNEXE
Environnements de développement
visuels
basés sur l’approche à composants
Préambule
Ce chapitre présente différentes technologies que nous avons découvertes et qui relèvent du déve-
loppement par assemblage de composants à travers un environnement visuel.
169
170 Annexe B. Environnements de programmation visuels basés sur l’approche à composants
Quartz Composer1 est un environnement de développement visuel fournit avec Mac Os X depuis
sa version Tiger (10.4).
B.2 Automator
http://www.apple.com/fr/macosx/features/automator/
http://www.apple.com/fr/macosx/theater/automator.html
B.3 Pipes
Pipes2 est une application web proposée par Yahoo! qui fournit une interface visuelle pour
construire des applications web en agrégeant des flux rss ou d’autres services provenant de sites dif-
férents. La définition proposée par le site officiel est la suivante :
« Pipes is a powerful composition tool to aggregate, manipulate, and mashup content from
around the web. »
B.4 Scratch
http://scratch.mit.edu
1 http://developer.apple.com/graphicsimaging/quartz/quartzcomposer.html
http://developer.apple.com/documentation/GraphicsImaging/Conceptual/QuartzComposer/index.html
2 http://pipes.yahoo.com
Bibliographie
[Abadi et Cardelli, 1996] Martin Abadi et Luca Cardelli. A Theory of Objects. Springer-Verlag New
York, Inc., Secaucus, NJ, USA, 1996.
[Achermann et al., 2001] Franz Achermann, Markus Lumpe, Jean-Guy Schneider, et Oscar Nierstrasz.
Piccola – a Small Composition Language. In Formal Methods for Distributed Processing – A Survey of
Object-Oriented Approaches, éditeurs Howard Bowman et John Derrick, pages 403–426. Cambridge
University Press, 2001.
[Aldrich et al., 2002a] Jonathan Aldrich, Craig Chambers, et David Notkin. Architectural reasoning in
archjava. In ECOOP ’02: Proceedings of the 16th European Conference on Object-Oriented Program-
ming, pages 334–367, London, UK, 2002. Springer-Verlag.
[Aldrich et al., 2002b] Jonathan Aldrich, Craig Chambers, et David Notkin. ArchJava: Connecting
Software Architecture to Implementation. In ICSE, pages 187–197. ACM, 2002.
[Aldrich et al., 2003] Jonathan Aldrich, Vibha Sazawal, Craig Chambers, et David Notkin. Language
support for connector abstractions. In ECOOP, éditeur Luca Cardelli, volume 2743 de Lecture Notes
in Computer Science, pages 74–102. Springer, 2003.
[Aldrich, 2003] J. Aldrich. Using Types to Enforce Architectural Structure. PhD thesis, University of
Washington, August 2003.
[Almeida et al., 2001] João Paulo A. Almeida, Marten Van Sinderen, et Lambert Nieuwenhuis. Trans-
parent Dynamic Reconfiguration for CORBA. In DOA ’01: Proceedings of the Third International
Symposium on Distributed Objects and Applications, page 197, Washington, DC, USA, 2001. IEEE
Computer Society.
[Anantharam, 2001] Parasuram Anantharam. Programming ruby. SIGSOFT Software Engineering
Notes, 26(4):89–89, 2001.
[Atkinson et al., 2001] Colin Atkinson, Barbara Paech, Jens Reinhold, et Torsten Sander. Developing
and Applying Component-Based Model-Driven Architectures in KobrA. edoc, 00:0212, 2001.
[Bachman et al., 2000] F. Bachman et al. Volume ii : Technical concepts of
component-based software engineering, 2nd edition. Rapport Technique 2,
The Software Engineering Institute (SEI), Carnegie Mellon University, mai 2000.
http://www.sei.cmu.edu/publications/documents/00.reports/00tr008/00tr008title.html.
171
172 Bibliographie
[Bálek et Plásil, 2001] Dusan Bálek et Frantisek Plásil. Software connectors and their role in com-
ponent deployment. In Proceedings of DAIS’01, Krakow, Poland, September 2001. Kluwer Academic
Publishers.
[Balek, 2002] Dusan Balek. Connectors in software architectures, 2002.
[Bastide et Barboni, 2006] Rémi Bastide et Eric Barboni. Software Components: a Formal Semantics
Based on Coloured Petri Nets. Electronic Notes in Theoretical Computer Science, 160:57–73, 2006.
[Bennouar et al., 2006] D. Bennouar, T. Khammaci, et A. Henni. The Design of a Complex Software
System with ArchJava. Journal of Computer Science, 2(11):807–814, september 2006. Science Pu-
blications.
[Beugnard et al., 1999] Antoine Beugnard, Jean-Marc Jézéquel, Noël Plouzeau, et Damien Watkins.
Making Components Contract Aware. Computer, 32(7):38–45, 1999.
[Beugnard, 2005] Antoine Beugnard. Peut-on réaliser des composants avec un langage á objets ? In
Proceedings of LMO’05 Conference (Langages et Modèles à Objets), pages 47–60, Bern, Switzerland,
March 2005. Hermes Editions.
[Birtwistle et al., 1973] Graham M. Birtwistle, Ole-Johan Dahl, Bjorn Myhrhaug, et Kristen Nygaard.
SIMULA Begin. Auerbach Publishers, Philadelphia, Penn., 1973.
[Black et al., 2007] Andrew Black, Stéphane Ducasse, Oscar Nierstrasz, et Damien Pollet. Squeak by
Example. Square Bracket Associates, 2007. with Damien Cassou and Marcus Denker.
[Bobrow et al., 1986] Daniel G. Bobrow, Kenneth Kahn, Gregor Kiczales, Larry Masinter, Mark Ste-
fik, et Frank Zdybel. Commonloops: merging lisp and object-oriented programming. In OOPLSA
’86: Conference proceedings on Object-oriented programming systems, languages and applications,
pages 17–29, New York, NY, USA, 1986. ACM Press.
[Bouraqadi, 1999] N. Bouraqadi. Un MOP Smalltalk pour l’étude de la composition et de la com-
patibilité des métaclasses. Application à la programmation par aspects (A Smalltalk MOP for the
Study of Metaclass Composition and Compatibility. Application to Aspect-Oriented Programming -
In French). Thèse de doctorat, Université de Nantes, Nantes, France, Juillet 1999.
[Briand et al., 1999] Lionel C. Briand, John W. Daly, et Jürgen K. Wüst. A Unified Framework for Cou-
pling Measurement in Object-Oriented Systems. IEEE Trans. Software Eng., 25(1):91–121, 1999.
[Brown et Wallnau, 1998] Alan W. Brown et Kurt C. Wallnau. The Current State of CBSE. IEEE Soft-
ware, 15(5):37–46, 1998.
[Broy et al., 1998] Manfred Broy, Anton Deimel, Juergen Henn, Kai Koskimies, Frantisek Plásil, Gustav
Pomberger, Wolfgang Pree, Michael Stal, et Clemens A. Szyperski. What characterizes a (software)
component? Software - Concepts and Tools, 19(1):49–56, 1998.
[Bruneton et al., 2002] E. Bruneton, T. Coupaye, et J.B. Stefani. The Fractal Composition Framework.
Rapport technique, The Object Web Consortium, Juillet 2002.
[Bruneton et al., 2004] Eric Bruneton, Thierry Coupaye, Matthieu Leclercq, Vivien Quéma, et Jean-
Bernard Stefani. An Open Component Model and Its Support in Java. In CBSE, éditeurs Ivica
Crnkovic, Judith A. Stafford, Heinz W. Schmidt, et Kurt C. Wallnau, volume 3054 de Lecture Notes in
Computer Science, pages 7–22. Springer, 2004.
Bibliographie 173
[Büchi et Weck, 1998] Martin Büchi et Wolfgang Weck. Compound types for Java. In OOPSLA’98:
Proceedings of the 13th ACM SIGPLAN conference on Object-Oriented Programming, Systems, Lan-
guages, and Applications, pages 362–373, New York, NY, USA, 1998. ACM Press.
[Cardelli, 1997] Luca Cardelli. The Handbook of Computer Science and Engineering, chapitre 103,
Type Systems, pages 2208–2236. CRC Press, Boca Raton, FL, 1997.
[Cheesman et Daniels, 2000] John Cheesman et John Daniels. UML components: a simple process for
specifying component-based software. Addison-Wesley Longman Publishing Co., Inc., Boston, MA,
USA, 2000.
[Choi, 2000] Jung Pil Choi. Aspect-Oriented Programming with Enterprise JavaBeans. edoc, 00:252,
2000.
[Cohen et Gil, 2004] Tal Cohen et Joseph Gil. AspectJ2EE = AOP + J2EE, volume 3086. January 2004.
[Cointe et al., 2004] Pierre Cointe, Jacques Noyé, Rémi Douence, Thomas Ledoux, Jean-Marc Me-
naud, Gilles Muller, et Mario Südholt. Programmation post-objets : des langages d’aspects aux
langages de composants. RSTI - L’objet, 10(4):119–143, 2004.
[Collet et al., 2005] Philippe Collet, Roger Rousseau, Thierry Coupaye, et Nicolas Rivierre. A Contrac-
ting System for Hierarchical Components. In Component-Based Software Engineering, 8th Inter-
national Symposium (CBSE’2005), volume 3489 de LNCS, pages 187–202, St-Louis (Missouri), USA,
Mai 2005. Springer Verlag.
[de Alfaro et Henzinger, 2001] Luca de Alfaro et Thomas A. Henzinger. Interface automata. In
ESEC/FSE-9: Proceedings of the 8th European software engineering conference held jointly with 9th
ACM SIGSOFT international symposium on Foundations of software engineering, pages 109–120,
New York, NY, USA, 2001. ACM Press.
[Desnos et al., 2007] Nicolas Desnos, Marianne Huchard, Christelle Urtado, Sylvain Vauttier, et Guy
Tremblay. Automated and unanticipated flexible component substitution. In Proceedings of the
10th ACM SIGSOFT Symposium on Component-Based Software Engineering (CBSE2007), éditeurs
H. W. Schmidt et al., volume 4608 de LNCS, pages 33–48, Medford, MA, USA, July 2007. Springer.
[Dijkstra, 1976] Edsger Wybe Dijkstra. A Discipline of Programming. Prentice Hall PTR, Upper Saddle
River, NJ, USA, 1976.
[Dony et al., 1992] Christophe Dony, Jacques Malenfant, et Pierre Cointe. Prototype-Based Lan-
guages: From a New Taxonomy to Constructive Proposals and Their Validation. In OOPSLA, pages
201–217, 1992.
[D’Souza et Wills, 1999] Desmond F. D’Souza et Alan Cameron Wills. Objects, components, and fra-
meworks with UML: the catalysis approach. Addison-Wesley Longman Publishing Co., Inc., Boston,
MA, USA, 1999.
174 Bibliographie
[Ducasse et al., 2006] Stéphane Ducasse, Tudor Gîrba, et Adrian Kuhn. Distribution Map. In Procee-
dings International Conference on Software Maintainance (ICSM 2006), pages 203–212, Los Alami-
tos CA, 2006. IEEE Computer Society.
[Duclos et al., 2002] Frédéric Duclos, Jacky Estublier, et Philippe Morat. Describing and using non
functional aspects in component based applications. In AOSD ’02: Proceedings of the 1st interna-
tional conference on Aspect-oriented software development, pages 65–75, New York, NY, USA, 2002.
ACM Press.
[Eugster et al., 2003] Patrick Th. Eugster, Pascal A. Felber, Rachid Guerraoui, et Anne-Marie Kermar-
rec. The many faces of publish/subscribe. ACM Computer Survey, 35(2):114–131, 2003.
[Fakih et al., 2004] Houssam Fakih, Noury Bouraqadi, et Laurence Duchien. Aspects and software
components: A case study of the FRACTAL component model. In International Workshop on
Aspect-Oriented Software Development (WAOSD 2004), éditeurs Minhuan Huang, Hong Mei, et
Jianjun Zhao, Septembre 2004.
[Flanagan, 1998] David Flanagan. JavaScript: The Definitive Guide. O’Reilly & Associates, Inc., Sebas-
topol, CA, USA, 1998.
[Flatt, 2000] Matthew Raymond Flatt. Programming languages for reusable software components.
PhD thesis, 2000. Adviser-Matthias Felleisen.
[Fröhlich et al., ] Peter H. Fröhlich, Andreas Gal, et Michael Franz. On Reconciling Objects, Compo-
nents, and Efficiency in Programming Languages.
[Fröhlich et al., 2005] Peter H. Fröhlich, Andreas Gal, et Michael Franz. Supporting software compo-
sition at the programming-language level. Science of Computer Programming, Special Issue on New
Software Composition Concept, 56(1-2):41–57, April 2005.
[Fröhlich, 2000] Peter H. Fröhlich. Component-Oriented Programming Languages: Messages vs. Me-
thods, Modules vs. Types. In Proceedings of the Workshop on Programming Languages and Com-
puter Architecture, Bad Honnef, Germany, Mai 2000. Technical Report 2007, Institute for Computer
Science and Applied Mathematics, Christian-Albrechts-University, Kiel, Germany.
[Gamma et al., 1995] Erich Gamma, Richard Helm, Ralph Johnson, et John Vlissides. Design Patterns :
Elements of Reusable Object-Oriented Software. Addison Wesley, March 1995.
[Garlan et al., 1995] David Garlan, Robert Allen, et John Ockerbloom. Architectural Mismatch, or,
Why it’s hard to build systems out of existing parts. In Proceedings of the 17th International Confe-
rence on Software Engineering, pages 179–185, Seattle, Washington, April 1995.
Bibliographie 175
[Garlan et Shaw, 1993] David Garlan et Mary Shaw. An Introduction to Software Architecture. In
Advances in Software Engineering and Knowledge Engineering, éditeurs V. Ambriola et G. Tortora,
pages 1–39, Singapore, 1993. World Scientific Publishing Company.
[Goldberg et Robson, 1989] Adele Goldberg et David Robson. Smalltalk-80: The Language. Addison-
Wesley Longman Publishing Co., Inc., Boston, MA, USA, 1989.
[Gröne et al., 2005] Bernhard Gröne, Andreas Knöpfel, et Peter Tabeling. Component vs. component:
Why we need more than one definition. In ECBS, pages 550–552. IEEE Computer Society, 2005.
[Hamilton, 1997] Graham Hamilton. JavaBeans. API Specification, Sun Microsystems, Juillet 1997.
Version 1.01.
[Hartel, 1999] Pieter H. Hartel. LETOS - a lightweight execution tool for operational semantics. Soft-
ware: Practice and Experience, 29(15):1379–1416, 1999.
[Hassine et al., 2003] I. Hassine, D. Rieu, A. Front-Conte, et F. Bounaas. Modélisation et formalisation
d’une démarche de développement à base de composants métier. TBA, 2003.
[Heineman et Councill, 2001] éditeurs George T. Heineman et William T. Councill. Component-based
software engineering: putting the pieces together. Addison-Wesley Longman Publishing Co., Inc.,
Boston, MA, USA, 2001.
[Hejlsberg et al., 2003] Anders Hejlsberg, Scott Wiltamuth, et Peter Golde. C# Language Specification.
Addison-Wesley Longman Publishing Co., Inc., Boston, MA, USA, 2003.
[Hürsch, 1994] Walter L. Hürsch. Should Superclass be Abstract? In Proceedings ECOOP’94 : 8th Euro-
pean Conf. Object-Oriented Programming, éditeurs M. Tokoro et R.Pareschi, volume 821 de LNCS,
pages 12–31. Springer Verlag, july 1994.
[Ibrahim, 1998] R. Ibrahim. COMEL: A formal model for COM, 1998.
[Ingalls et al., 1997] Dan Ingalls, Ted Kaehler, John Maloney, Scott Wallace, et Alan Kay. Back to the
future: the story of Squeak, a practical Smalltalk written in itself. In OOPSLA ’97: Proceedings of the
12th ACM SIGPLAN conference on Object-oriented programming, systems, languages, and applica-
tions, pages 318–326, New York, NY, USA, 1997. ACM Press.
[Jézéquel et Meyer, 1997] Jean-Marc Jézéquel et Bertrand Meyer. Design by Contract: The Lessons of
Ariane. Computer, 30(1):129–130, 1997.
[Kiczales et al., 1997] Gregor Kiczales, John Lamping, Anurag Mendhekar, Chris Maeda, Cristina
Lopes, Jean-Marc Loingtier, et John Irwin. Aspect-oriented programming. In 11th Europeen Conf.
Object-Oriented Programming, éditeurs Mehmet Akşit et Satoshi Matsuoka, volume 1241 de LNCS,
pages 220–242. Springer Verlag, 1997.
[Kiczales et al., 2001] Gregor Kiczales, Erik Hilsdale, Jim Hugunin, Mik Kersten, Jeffrey Palm, et
William G. Griswold. An Overview of AspectJ. In ECOOP, éditeur Jørgen Lindskov Knudsen, vo-
lume 2072 de Lecture Notes in Computer Science, pages 327–353. Springer, 2001.
[Krasner et Pope, 1988] Glenn E. Krasner et Stephen T. Pope. A cookbook for using the model-view-
controller user interface paradigm in smalltalk-80. In Journal of Object-Oriented Programming,
volume 1, pages 26–49, Août-Septembre 1988.
176 Bibliographie
[Lahire et al., 2004] Ph. Lahire, G. Arévalo, H. Astudillo, A.P. Black, E. Ernst, M. Huchard, T. Oplustil,
M. Sakkinen, et P. Valtchev. Mechanisms for Specialization, Generalization and Inheritance, 2004.
[Lahire et Quintian, 2006] Philippe Lahire et Laurent Quintian. New Perspective To Improve Reusa-
bility in Object-Oriented Languages. Journal Of Object Technology (JOT), 5(1):117–138, 2006.
[Langelier et al., 2005] Guillaume Langelier, Houari A. Sahraoui, et Pierre Poulin. Visualisation et
analyse de logiciels de grande taille. In Langages et Modèles à Objets 2005, pages x–y, Mars 2005.
[Lau et Wang, 2005a] K. K. Lau et Z. Wang. A Survey of Software Component Models. Technical re-
ports, Department of Computer Science, University of Manchester, Avril 2005.
[Lau et Wang, 2005b] Kung-Kiu Lau et Zheng Wang. A taxonomy of software component models. In
EUROMICRO-SEAA, pages 88–95. IEEE Computer Society, 2005.
[Lehman et Belady, 1985] éditeurs M. M. Lehman et L. A. Belady. Program evolution: processes of
software change. Academic Press Professional, Inc., San Diego, CA, USA, 1985.
[Leon, 2006] Ramon Leon, Octobre 2006. http://feeds.feedburner.com/~r/onsmalltalk/KhHm/
~3/43846539/.
[Lieberherr et al., 1999] K. Lieberherr, D. Lorenz, et M. Mezini. Programming with Aspectual Compo-
nents, 1999.
[Lieberherr et al., 2003] Karl Lieberherr, David H. Lorenz, et Johan Ovlinger. Aspectual Collabora-
tions: Combining Modules and Aspects. The Computer Journal, 46(5):542–565, Septembre 2003.
[Lieberman, 1986] Henry Lieberman. Using prototypical objects to implement shared behavior in
object-oriented systems. In OOPLSA ’86: Conference proceedings on Object-oriented programming
systems, languages and applications, pages 214–223, New York, NY, USA, 1986. ACM Press.
[Loulergue, 2004] Frédéric Loulergue. Développement d’applications avec Objective CAML by E.
Chailloux, P. Manoury and B. Pagano, O’Reilley, 2003. Journal of Functional Programming,
14(5):592–594, 2004.
[Luckham et al., 1995] David C. Luckham, James Vera, et Sigurd Meldal. Three Concepts of System
Architecture. Rapport Technique CSL-TR-95-674, 1995.
[Luckham et Vera, 1995] David C. Luckham et James Vera. An Event-Based Architecture Definition
Language. IEEE Trans. Software Eng., 21(9):717–734, 1995.
[Léger et al., 2006] Marc Léger, T. Coupaye, et Thomas Ledoux. Contrôle dynamique de l’intégrité
des communications dans les architectures à composants. In Langages et Modèles à Objets, éditeur
S. Vauttier R. Rousseau, C. Urtado, pages 21–36. Hermès-Lavoisier, 2006.
[Magee et Kramer, 1996] Jeff Magee et Jeff Kramer. Dynamic structure in software architectures. In
SIGSOFT ’96: Proceedings of the 4th ACM SIGSOFT symposium on Foundations of software enginee-
ring, pages 3–14, New York, NY, USA, 1996. ACM Press.
[Marvie, 2002] Raphael Marvie. Séparation des préoccupations et méta-modélisation pour environ-
nements de manipulation d’architectures logicielles à base de composants. PhD thesis, Université
des Sciences et Techniques de Lille, LIFL, Villeneuve d’Ascq, Décembre 2002.
Bibliographie 177
[McDirmid et al., 2001a] S. McDirmid, M. Flatt, et W. Hsieh. Mixing COP and OOP, 2001.
[McDirmid et al., 2001b] Sean McDirmid, Matthew Flatt, et Wilson C. Hsieh. Jiazzi: New-age compo-
nents for old-fashioned java. In OOPSLA, pages 211–222, 2001.
[McDirmid et Hsieh, 2006] Sean McDirmid et Wilson C. Hsieh. Superglue: Component programming
with object-oriented signals. In ECOOP, éditeur Dave Thomas, volume 4067 de Lecture Notes in
Computer Science, pages 206–229. Springer, 2006.
[McIlroy, 1968] M. D. McIlroy. Mass produced software components. In Proceedings, NATO Confe-
rence on Software Engineering, éditeurs P. Naur et B. Randell, Garmisch, Germany, Octobre 1968.
[Medvidovic et al., 2002] Nenad Medvidovic, David S. Rosenblum, David F. Redmiles, et Jason E. Rob-
bins. Modeling software architectures in the Unified Modeling Language. ACM Trans. Software Eng.
Methodol., 11(1):2–57, 2002.
[Medvidovic et Taylor, 2000] Nenad Medvidovic et Richard N. Taylor. A Classification and Compari-
son Framework for Software Architecture Description Languages. Software Engineering, 26(1):70–
93, 2000.
[Mehta et al., 2000] Nikunj R. Mehta, Nenad Medvidovic, et Sandeep Phadke. Towards a taxonomy
of software connectors. In ICSE ’00: Proceedings of the 22nd international conference on Software
engineering, pages 178–187, New York, NY, USA, 2000. ACM Press.
[Mei, 2004] Hong Mei. ABC: Supporting Software Architectures in the Whole Lifecycle. In SEFM ’04:
Proceedings of the Software Engineering and Formal Methods, Second International Conference on
(SEFM’04), pages 342–343, Washington, DC, USA, 2004. IEEE Computer Society.
[Mernik et al., 2005] Marjan Mernik, Jan Heering, et Anthony M. Sloane. When and how to develop
domain-specific languages. ACM Computer Survey, 37(4):316–344, 2005.
[Mezini et Ostermann, 2003] Mira Mezini et Klaus Ostermann. Conquering aspects with Caesar. In
AOSD ’03: Proceedings of the 2nd international conference on Aspect-oriented software development,
pages 90–99, New York, NY, USA, 2003. ACM Press.
[Microsoft, 1995] Microsoft. The Component Object Model Specification, 1995.
[Microsoft, 1996] Microsoft. DCOM technical overview. Microsoft Windows NT Server white paper,
Microsoft Corporation, 1996.
[Mikhajlov et Sekerinski, 1998] Leonid Mikhajlov et Emil Sekerinski. A Study of the Fragile Base Class
Problem. Lecture Notes in Computer Science, 1445:355+, 1998.
[Minsky, 1975] M. Minsky. A Framework for Representing Knowledge. In The Psychology of Computer
Vision, éditeur P. Winston, pages 211–281. mgh, ny, 1975.
[Moon, 1986] David A. Moon. Object-oriented programming with flavors. In OOPLSA ’86: Conference
proceedings on Object-oriented programming systems, languages and applications, pages 1–8, New
York, NY, USA, 1986. ACM Press.
[Moriconi et al., 1995] Mark Moriconi, Xiaolei Qian, et R. A. Riemenschneider. Correct Architecture
Refinement. IEEE Trans. Software Eng., 21(4):356–3, 1995.
178 Bibliographie
[Nierstrasz et al., 2005] Oscar Nierstrasz, Alexandre Bergel, Marcus Denker, Stéphane Ducasse, Mar-
kus Gälli, et Roel Wuyts. On the revival of dynamic languages. In Proceedings of Software Com-
position 2005, éditeurs Thomas Gschwind et Uwe Aßmann, volume 3628, pages 1–13. LNCS 3628,
2005. Invited paper.
[Nierstrasz et al., 2006] Oscar Nierstrasz, Stéphane Ducasse, et Nathanael Schärli. Flattening Traits.
Journal of Object Technology, 5(4):129–148, may 2006.
[Nierstrasz et Dami, 1995] Oscar Nierstrasz et Laurent Dami. Component-oriented software tech-
nology. In Object-Oriented Software Composition, éditeurs Oscar Nierstrasz et Dennis Tsichritzis,
pages 3–28. Prentice-Hall, 1995.
[Object Management Group, 2002] Object Management Group. Manual of Corba Component Model
V3.0, 2002. http://www.omg.org/technology/documents/formal/components.htm.
[Odersky et Zenger, 2005] Martin Odersky et Matthias Zenger. Scalable Component Abstractions. In
OOPSLA, éditeurs Ralph Johnson et Richard P. Gabriel, pages 41–57. ACM, 2005.
[OMG, 2004] Object Management Group OMG. Uml 2.0 superstructure specification. Rapport tech-
nique, Object Management Group, 2004.
[Ommering, 1998] Rob C. Van Ommering. Koala, a component model for consumer electronics pro-
duct software. In ESPRIT ARES Workshop, éditeur Frank van der Linden, volume 1429 de Lecture
Notes in Computer Science, pages 76–86. Springer, 1998.
[Opluštil, 2003] Tomáš Opluštil. Inheritance in Architecture Description Languages. In WDS 2003 -
Proceedings of Contributed Papers, éditeur Jana Šafránková, pages 124–131, Prague, Czech Repu-
blic, 2003. Matfyzpress, MFF UK.
[Oussalah et al., 2006] M. Oussalah, N. Sadou, et D. Tamzalit. SAEV :a Model to Face Evolution Pro-
blem in Software Architecture. In Proceedings of the International ERCIM Workshop on Software
Evolution, pages 137–146, Lille, France, april 2006.
[Oussalah, 2005] M. Oussalah. Ingénierie des composants : concepts, techniques et outils. Editions
Vuibert, 2005.
[Parnas, 1972] D. L. Parnas. On the Criteria To Be Used in Decomposing Systems into Modules.
Comm. ACM, 15(12):1053–1058, Décembre 1972.
[Passama, 2006] Robin Passama. Conception et développement de contrôleurs de robots - Une métho-
dologie basée sur les composants logiciels. PhD thesis, Université de Montpellier 2, LIRMM, Juin
2006.
[Pavel et al., 2005] Sebastian Pavel, Jacques Noyé, et Jean-Claude Royer. Un modèle de composant
avec protocole symbolique. In Journée du groupe Objets, Composants et Modèles, Bern, Suisse,
2005.
[Pawlak et al., 2004] Renaud Pawlak, Jean-Philippe Retaillé, et Lionel Seinturier. Programmation
orientée aspect pour Java/J2EE. Eyrolles, 2004.
[Perry et Wolf, 1992] Dewayne E. Perry et Alexander L. Wolf. Foundations for the study of software
architecture. SIGSOFT Software Engineering Notes, 17(4):40–52, 1992.
Bibliographie 179
[Peschanski et al., 2000] F. Peschanski, T. Meurisse, et J.-P. Briot. Les composants logiciels : Evolution
technologique ou nouveau paradigme ? In In Actes de la conférence OCM’2000, pages 53–65, 2000.
[Pessemier et al., 2006] N. Pessemier, L. Seinturier, L. Duchien, et T. Coupaye. A model for developing
component-based and aspect-oriented systems. In Proceedings of the 5th International Sympo-
sium on Software Composition (SC’06), volume 4089 de Lecture Notes in Computer Science. Sprin-
ger, Mars 2006.
[Plásil et al., 1998] F. Plásil, D. Bálek, et R. Janecek. Sofa/dcup: Architecture for component trading
and dynamic updating. In CDS ’98: Proceedings of the International Conference on Configurable
Distributed Systems, page 43, Washington, DC, USA, 1998. IEEE Computer Society.
[Plásil et al., 1999] Frantisek Plásil, Miloslav Besta, et Stanislav Visnovsky. Bounding component be-
havior via protocols. In TOOLS ’99: Proceedings of the Technology of Object-Oriented Languages
and Systems, page 387, Washington, DC, USA, 1999. IEEE Computer Society.
[Plásil et Visnovsky, 2002] Frantisek Plásil et Stanislav Visnovsky. Behavior protocols for software
components. IEEE Trans. Software Eng., 28(11):1056–1076, 2002.
[Pollet et al., 2007] Damien Pollet, Stephane Ducasse, Loic Poyet, Ilham Alloui, Sorana Cimpan, et
Herve Verjus. Towards A Process-Oriented Software Architecture Reconstruction Taxonomy. In
CSMR ’07: Proceedings of the 11th European Conference on Software Maintenance and Reenginee-
ring, pages 137–148, Washington, DC, USA, 2007. IEEE Computer Society.
[Privat, 2006] Jean Privat. De l’expressivité à l’efficacité, une approche modulaire des langages à objets.
Le langage PRM et le compilateur prmc. PhD thesis, Université de Montpellier 2, LIRMM, Juillet
2006.
[Pulvermüller et al., 2001] Elke Pulvermüller, Andreas Speck, Maja D’Hondt, Wolfgang DeMeuter, et
James O. Coplien. Feature interaction in composed systems, ecoop 2001 - proceedings, tr no. 2001-
14. Rapport Technique 2001-14, Sep 2001.
[Riveill et Merle, 2000] Michel Riveill et Philippe Merle. La programmation par composants. In La
programmation par composants, numéro H2759. Techniques de l’Ingénieurs, Décembre 2000.
[Rogerson, 1997] Dale Rogerson. Inside COM. Microsoft Press, Redmond, WA, USA, 1997.
[Sametinger, 1997] Johannes Sametinger. Software engineering with reusable components. Springer-
Verlag New York, Inc., New York, NY, USA, 1997.
[Sanen et al., 2006] Frans Sanen, Eddy Truyen, Wouter Joosen, Andrew Jackson, Andronikos Nedos,
Siobhan Clarke, Neil Loughran, et Awais Rashid. Classifying and documenting aspect interactions.
In Proceedings of the Fifth AOSD Workshop on Aspects, Components, and Patterns for Infrastructure
Software, éditeurs Yvonne Coady, David H. Lorenz, Olaf Spinczyk, et Eric Wohlstadter, pages 23–
26, Bonn, Germany, Mars 2006. Published as University of Virginia Computer Science Technical
Report CS–2006–01.
[Schmidt, 2006] Douglas C. Schmidt. Model-driven engineering. IEEE Computer, 39(2):25–31, 2006.
[Schärli et al., 2003] Nathanael Schärli, Stéphane Ducasse, Oscar Nierstrasz, et Andrew Black. Traits:
Composable Units of Behavior. In Proceedings of European Conference on Object-Oriented Pro-
gramming (ECOOP’03), volume 2743 de LNCS, pages 248–274. Springer Verlag, July 2003.
180 Bibliographie
[Seco et Caires, 2000] João Costa Seco et Luís Caires. A basic model of typed components. Lecture
Notes in Computer Science, 1850:108–129, 2000.
[Shaw et al., 1995] Mary Shaw, Robert DeLine, Daniel V. Klein, Theodore L. Ross, David M. Young, et
Gregory Zelesnik. Abstractions for Software Architecture and Tools to Support Them. Software
Engineering, 21(4):314–335, 1995.
[Shaw, 1996] Mary Shaw. Procedure Calls Are the Assembly Language of Software Interconnection:
Connectors Deserve First-Class Status. In ICSE ’93: Selected papers from the Workshop on Studies
of Software Design, pages 17–32, London, UK, 1996. Springer-Verlag.
[Snyder, 1987] Alan Snyder. Inheritance and the development of encapsulated software systems. In
Research Directions in Object-Oriented Programming, pages 165–188. 1987.
[Souchon, 2005] Frédéric Souchon. SaGE, Un Système de Gestion d’Exceptions pour la Programma-
tion Orientée Message : Le Cas des Systèmes Multi-Agents et des Plates-Formes à Base de Composants
Logiciels. PhD thesis, Université de Montpellier 2, LIRMM, 2005.
[Standard, 2001] ISO/IEC Standard. Software Engineering – Product Quality – Part 1: Quality Model.
ISO Standard 9126-1, ISO/IEC, 2001. http://iso.org.
[Stein, 1987] Lynn Andrea Stein. Delegation is inheritance. In OOPSLA ’87: Conference proceedings
on Object-oriented programming systems, languages and applications, pages 138–146, New York,
NY, USA, 1987. ACM Press.
[Stoerzer et Hanenberg, 2005] Maximilan Stoerzer et Stefan Hanenberg. Software Engineering Pro-
perties of Languages and Aspect Technologies. In Software Engineering Properties of Languages
and Aspect Technologies, éditeurs Lodewijk Bergmans, Kris Gybels, Peri Tarr, et Erik Ernst, Mars
2005.
[Suvée et al., 2005] Davy Suvée, Bruno De Fraine, et Wim Vanderperren. FuseJ: An architectural des-
cription language for unifying aspects and components. In Software Engineering Properties of Lan-
guages and Aspect Technologies, éditeurs Lodewijk Bergmans, Kris Gybels, Peri Tarr, et Erik Ernst,
Mars 2005.
[Suvée et al., 2006] Davy Suvée, Bruno De Fraine, et Wim Vanderperren. A symmetric and unified
approach towards combining aspect-oriented and component-based software development. In
CBSE, éditeurs Ian Gorton, George T. Heineman, Ivica Crnkovic, Heinz W. Schmidt, Judith A. Staf-
ford, Clemens A. Szyperski, et Kurt C. Wallnau, volume 4063 de Lecture Notes in Computer Science,
pages 114–122. Springer, 2006.
[Szyperski, 2002] C. Szyperski. Component Software: Beyond Object-Oriented Programming (2nd Edi-
tion). Addison-Wesley, 2002.
[Taenzer et al., 1989] D. Taenzer, M. Ganti, et S. Podar. Problems in Object-Oriented Software Reuse.
In Proceedings of ECOOP’89 : European Conf. Object-Oriented Programming, éditeur S. Cook, vo-
lume 821, pages 25–38. Cambridge University Press, july 1989.
[Traverson et Yahiaoui, 2002] Bruno Traverson et Nesrine Yahiaoui. Connectors for CORBA Compo-
nents. In OOIS ’02: Proceedings of the 8th International Conference on Object-Oriented. Information
Systems, pages 458–463, London, UK, 2002. Springer-Verlag.
Bibliographie 181
[Tremblay et Chae, 2005] Guy Tremblay et Junghyun Chae. Towards specifying contracts and proto-
cols for Web services. In MCeTech Montreal Conference on eTechnologies, éditeurs H. Mili et F. Khen-
dek, pages 73–85, January 2005.
[Ungar et Smith, 1987] David Ungar et Randall B. Smith. Self: The power of simplicity. In OOPSLA
’87: Conference proceedings on Object-oriented programming systems, languages and applications,
pages 227–242, New York, NY, USA, 1987. ACM Press.
[WCO, 1996] Workshop on Component-Oriented Programming (WCOP’96). Springer-Verlag, 1996.
[Weck et Szyperski, 1996] W. Weck et C. Szyperski. Do we need inheritance, 1996.
[Wettel et Lanza, 2007] Richard Wettel et Michele Lanza. Visualizing Software Systems as Cities. In
VISSOFT’07 (4th IEEE International Workshop on Visualizing Software For Understanding and Ana-
lysis), éditeur IEEE CS Press, pages 92–99, 2007.
[Wuyts et Ducasse, 2001] Roel Wuyts et Stéphane Ducasse. Composition Languages for Black-Box
Components. In First OOPSLA Workshop on Language Mechanisms for Programming Software
Components, 2001.
[Zenger, 2002] Matthias Zenger. Type-safe prototype-based component evolution. In Proceedings of
the European Conference on Object-Oriented Programming, Malaga, Spain, June 2002.
[Zenger, 2005] Matthias Zenger. Keris: evolving software with extensible modules: Research articles.
J. Software Maint. Evol., 17(5):333–362, 2005.
Publications
[Fabresse et al., 2004] Luc Fabresse, Christophe Dony, Marianne Huchard, et Olivier Pichon. Vers des
composants logiciels interfaçables. In 8ème Colloque Agents Logiciels, Coopération, Apprentissage
et Activité humaine (ALCAA’04), pages 33–48, 17–18 Juin 2004.
[Fabresse et al., 2006a] Luc Fabresse, Christophe Dony, et Marianne Huchard. Connexion non-
anticipée de composants en S CL : Une voie pour l’évolution des logiciels. In Atelier sur l’Evolution
du Logiciel, pages 1–7, 21 Mars 2006.
[Fabresse et al., 2006b] Luc Fabresse, Christophe Dony, et Marianne Huchard. Unanticipated
Connection of Components based on their State Changes Notifications. In International Work-
shop on Evaluation and Evolution of Component Composition (EECC’06). In proceedings of 18th
International Conference on Software Engineering and Knowledge Engineering (SEKE 2006), pages
702–708. Knowledge System Institute (KSI), 5–7 July 2006.
[Fabresse et al., 2007a] Luc Fabresse, Christophe Dony, et Marianne Huchard. Foundations of a
Simple and Unified Component-Oriented Language. Journal of Computer Languages, Systems &
Structures, 2007. To appear. doi :10.1016/j.cl.2007.05.002.
[Fabresse et al., 2007b] Luc Fabresse, Christophe Dony, et Marianne Huchard. S CL : a Simple, Uni-
form and Operational Language for Component-Oriented Programming in Smalltalk. In Advances
in Smaltalk, Proceedings of 14th International Smalltalk Conference (ISC), September 4–8, 2006,
éditeur De Meuter, Wolfgang, volume 4406, pages 91–110. LNCS, Springer-Verlag, April 2007.
[Fabresse, 2004] Luc Fabresse. Programmation de composants interfaçables. In Actes de JOCM, Jour-
nées du groupe Objets, Composants et Modèles, pages 19–24, 16 Mars 2004.
183
Résumé : Les composants logiciels sont des entités logicielles réutilisables promettant une réduction des coûts
liés au développement, à la maintenance et à l’évolution d’un logiciel. Actuellement, de nombreuses propositions
se réclament du mode de développement par assemblage de composants logiciels. Malgré un vocabulaire parfois
commun (composant, port, interface, service, connexion, connecteur), ces propositions sont disparates de par
leurs origines, leurs objectifs, leurs concepts ou encore leurs mécanismes. De plus, elles restent difficiles à utiliser
en pratique par manque de véritables langages de programmation sémantiquement fondés et concrètement utili-
sables. Devant tant de profusion, nous nous intéressons, dans cette thèse, aux problématiques suivantes : qu’est-ce
qu’un langage à composants ? Quels sont les avantages de ces langages ? Comment réaliser un langage à compo-
sants ?
Cette thèse propose donc S CL, un langage de programmation à composants permettant de mettre en pratique réel-
lement la programmation par composants (PPC). De par sa conception, S CLse veut être un langage : (i) minimal
dans le sens où toutes les abstractions et tous les mécanismes qui lui sont intégrés répondent à un besoin identi-
fié ; (ii) simple car ses abstractions et ses mécanismes sont de haut niveau ; (iii) détaillé car nous avons abordé un
ensemble de questions souvent oubliées dans les autres propositions comme l’auto-référence, le passage d’argu-
ments ou le statut des composants de base (collections, entiers, etc) dans un monde unifié ; (iv) dédié à la PPC, car
prenant en compte les deux préoccupations que nous jugeons majeures en PPC et qui sous-tendent toute l’étude :
le découplage et la non-anticipation.
Le coeur de S CLrepose principalement sur les concepts de composant, de port, de service, de connecteur et de
code glue ainsi que sur les mécanismes de liaison de ports et d’invocation de service. La séparation des préoccu-
pations au niveau du code occupe une part importante de l’étude qui établit un lien entre la programmation par
composants et la programmation par aspects (PPA). S CLpropose dans ce cadre une amélioration des approches
dites « symétriques » de la PPA, via une unification des concepts d’aspect et de composant et via un ensemble de
différents types de liaisons de ports qui permettent d’utiliser un même composant de façon standard ou de fa-
çon transversale. S CLintègre aussi un mécanisme général permettant d’établir des connexions entre composants
basées sur les changements d’états de leurs propriétés sans que leurs programmeurs n’aient à écrire une ligne de
code spécifique à cet effet. Deux prototypes de S CLsont également présentés, le premier et le plus abouti est écrit
en Smalltalk et le second en Ruby.
Mots clés : Langage à composants, séparation des préoccupations, découplage, assemblage non-anticipé, S CL
Abstract: Component-based software development (CBSD) promises costs reduction during the development, the
maintenance and the evolution of a software. Currently, a lot of propositions claim to be "component-oriented"
but they generally differ in several points like their objectives, their abstractions and their mecanisms. Moreover,
it is difficult to use CBSD in practice because of the lack of semantically founded and really usable component-
oriented languages (COL). In this context, we address the folowing problematics: What is a COL? What are the
advantages of those languages? How to realise a COL?
This work purposes the programming language S CL to enable component-oriented programming (COP) in prac-
tice. S CL has been built to be: (i) miminal because all its abstractions and mechanisms respond to an identified
need; (ii) simple because these abstractions and mechanisms are of a high-level; (iii) detailled because during the
conception of S CL we study a lot of crucial points usually forgotten by other propositions such as self-references,
arguments passing based on connections or considering base components (collections, integer, etc) in a unified
world; (iv) dedicated to COP because it integrates the two key points that we identified: decoupling and unantici-
pation.
The core of S CL is built upon the following concepts: component, port, service, connector, glue code and the fol-
lowing mechanisms: port binding, service invocation. We also study the separation of concerns in the source code.
In this area, S CL improves the "symmetric" aspect-oriented approaches and enables the use of a component as a
regular component or a crosscutting component. Deeper in the separation of concerns, S CL also enables com-
ponent connections based on state changes of their properties. Component programmers do not have to write
special code in order to enable this kind of connections. Currently, two prototypes of S CL are available. The more
advanced one is written in Smalltalk and the other one has been started in Ruby.
Keywords: Component-oriented language, separation of concerns, decoupling, unanticipated assembly, S CL