mars
2010
Bonsoir,
je suis en train de travailler sur la « bundlisation » de JTheque Core. Comme le core proposait un certain nombre de services, j’ai commencé par découper le gros jar qu’était le core en une série de bundles offrant tous un service bien précis.
Je me suis vite rendu compte que le découplage de mes différents services était à peu près nul. J’avais un nombre énorme de dépendances pour chaque bundle et pire encore, j’avais un grand nombre de dépendances cycliques, soit directs soit indirects.
Pour information, une dépendance cyclique directe est une situation dans laquelle un bundle X dépend d’un bundle Y qui dépend lui-même de X. Une dépendance cycle indirecte est la situation dans laquelle X dépend Y, Y dépend de Z et Z dépend de X. Je parle ici de bundles, mais cela peut concerner des projets, des classes ou des packages.
A noter que les différentes techniques que je vais présenter s’appliquent également pour des dépendances entre composants non-OSGi, les principes sont tout à fait les mêmes.
Dans le cas de packages à l’intérieur d’une même application, même s’il faut éviter cela, il est possible de vivre. Néanmoins, dans le cas de bundle ou de projets, il n’est en général même pas possible de builder l’application. Dans mon cas, les bundles sont des modules Maven. Il est donc impossible de lancer le build d’un bundle dans un cycle puisqu’il faut builder le premier bundle avant le second et le second avant le premier, ce qui n’est pas évidemment pas possible.
En plus d’être très mauvais dans un système de build, les dépendances cycliques entre componsants sont aussi un énorme problème de conception. En effet, on ne peut plus travailler sur le bundle A sans travailler sur le bundle B et vice-versa. En plus de cela, les deux composants sont très difficiles à faire évoluer de manière saine.
Les dépendances cycliques au niveau des classes à l’intérieur d’un composant ne posent pas vraiment de problèmes. On est toujours dans le même composant, les classes peuvent dont être liées de manière cyclique, même s’il faudrait tout de même éviter cela dès que c’est possible (ça ne l’est pas toujours).
Pour en revenir à JTheque, j’ai donc du résoudre ce problème de dépendances cycliques avant de pouvoir avancer plus loin. Il existe beaucoup de techniques permettant de faire cela. Voici les différentes pistes que j’ai explorées pour résoudre ces dépendances. A noter que ces techiques ne permettent pas seulement de règler le problème des dépendances cycliques mais permet aussi d’améliorer l’architecture de son application.
1. Déplacer les classes au bon endroit : Dans certains cas, j’avais des classes dans un bundle alors qu’elles n’avait rien à y faire. Dans ce cas-là, il peut suffir de les dépacer dans un autre bundle. A noter que ce cas est le plus simple à résoudre et que ce n’est malheureusement pas le plsu courant. En plus de cela, dans le cas ou la/les classes à déplacer sont dépendantes d’autres bundles, il est possible que cela crée de nouvelles dépendances cycliques. Un exemple est une classe de gestion de modules qui était contenues dans le bundle d’interfaces graphiques.
2. Déplacer les fonctionnalités : J’ai également trouvé beaucoup de fonctionnalités, typiquement des fonctions, à l’intérieur d’un bundle qui n’étaient pas au bon endroit. Par exemple le bundle « modules » permettait de faire des mises à jour alors qu’un bundle « update » existait. Il m’a suffi de déplacer cette fonctionnalité de mise à jour vers le bundle update pour résoudre ma dépendance.
3. Fonctionnalités mal découpées : Il arrive souvent qu’une fonctionnalité, généralement une méthode ou une classe, fasse trop de choses et entraîne des dépendances qui ne sont pas bienvenues ici. Souvent bien redécouper les fonctionnalités et donc le rôle de chaque classe/méthode peut suffir à régler le problème. A noter que ceci ne concerne pas uniquement les concepts de dépendances, mais est un principe de base de la conception orientée objet. Dans le cas de JTheque, j’avais pas par exemple, une permettant permettant de choisir une collection qui retournait true si on pouvait ouvrir la collection avec le couple login/password entré sinon false. Mais en plus de cela, elle affichait une erreur dans la vue de choix de collection s’il y avait un problème alors que ce n’était pas du son rôle. J’ai mis à jour cette méthode pour qu’elle ne fasse que tester si on pouvait ouvrir la collection ou non et j’ai implémenté l’affichage de l’erreur au niveau de l’action Swing.
4. Découper les fonctionnalités du bundle : Il peut arriver que le bundle fasse trop de choses ou ait plusieurs aspects bien distincts. Dans ce cas, il faut tout de suite séparer le bundle en plusieurs autres pour que chacun ait une responsabilité claire. Souvent cela va aider à résoudre une dépendance cyclique, comme moins de bundles auront des dépendances vers chacun des nouveaux bundles et que chacun des nouveaux bundles devrait avoir moins de dépendances vers d’autres bundles. Mais cela peut également introduire de nouveaux cycles car on peut mal séparer les composants et se retrouver avec 2 composants cycliquement dépendant, on n’aura alors fait que déplacer le problème. Dans JTheque, j’ai découpé le modules vues en 2 deux bundles : vues et ui. Le module vues contient l’implémentation des vues de JTheque alors que le module ui contient des classes utilitaires pour la création de vues ainsi que des composants Swing génériques qui peuvent être utilisés ailleurs.
5. Introduire un système de CallBack : Un système de CallBack est un simple système de listeners qui peut permettre de résoudre des dépendances. Au lieu qu’un bundle avertisse directement un autre bundle, il suffit que le second s’enregistre sur le premier comme listener. Ainsi le premier n’a plus besoin de connaître le second. On voit tout de suite que les listeners sont donc plus puissants qu’on ne pourrait le croire au premier abord. Dans le cas de JTheque, j’ai implémenté cela pour le choix des collections. En effet, de base, le gestionnaire de collections appelait directement la vue pour dire que la collection avait été choisie. En passant à un listener, cela est beaucoup plus souple et le gestionnaire de collections avertit automatiquement tous les écouteurs. En plus de cela, si j’ai besoin plus tard d’ajouter un second écouteur, je n’ai rien à changer du côté du bundle collections.
6. Séparer la spécification de l’implémentation : Comme il n’est pas toujours possible de supprimer les dépendances cycliques directement, on peut donc les contourner en séparant la partie de spécification de la partie d’implémentation. Cette technique n’est clairement pas la meilleur mais peut parfois être utile quand il est vraiment difficile de résoudre le cycle. On sépare donc dans un bundle toute la partie spécification qui est appelée de l’extérieur et la partie d’implémentation qui peut alors avoir des dépendances avec l’extérieur. A noter que ce n’est clairement pas souvent faisable en l’état car il n’est pas toujours possible de complètement séparer l’implémentation de la spécification sans cycles. Je n’ai pas utilisé cette technique dans JTheque, mais j’y ai pensé.
7. Regrouper 2 bundles en un seul : Dans certains cas, on se rend compte que deux modules sont si intrinsèquement liés qu’ils ne forment en fait qu’un seul modules. Dans ce cas, la meilleure solution est de les regrouper en un seul bundle.
Voilà, j’ai maintenant passé en revue les différentes techniques que j’ai utilisées pour résoudre les dépendances cycliques dans les bundles de JTheque Core. Il en existe très certainement de nombreuses autres, mais personnellement, elles m’ont suffit.
J’espère que cela pourra être utile aux personnes cherchant à résoudre des problèmes de dépendances cycliques.
6 Commentaires + Ajouter un commentaire
Archives
- novembre 2011
- avril 2010
- mars 2010
- février 2010
- janvier 2010
- décembre 2009
- novembre 2009
- octobre 2009
- septembre 2009
- juillet 2009
- juin 2009
- avril 2009
- mars 2009
- février 2009
- octobre 2008
- septembre 2008
- mars 2008
- février 2008
- janvier 2008
- décembre 2007
- novembre 2007
- octobre 2007
- septembre 2007
- août 2007
- juillet 2007
- juin 2007
- mai 2007
- avril 2007
Catégories
- AMD
- Apple
- Cartes graphiques
- Chrome
- Conception
- Divers
- Eclipse
- English
- Hardware
- Informatique générale
- Intégration continue
- IntelliJ Idea
- Java
- JTheque
- Linux
- Logiciels
- Mes articles
- Mes critiques de livres
- Mes projets
- Microsoft
- Mon serveur perso
- Office 2007
- Open Source
- Outils
- Perso
- PHP
- Processeurs
- Programmation
- Sécurité
- Spring
- Windows Vista
- Windows XP
Je ne fait la distinction que tu fais.
Pour moi, la puissance des listeners viennent justement de la séparation Spécification (Interface des listeners) de l’implémentation (classe qui implémente le listener).
Si le système d’écouteurs ne passait pas par cette séparation claire (via les interfaces qui spécifient un contrat mais non une implémentation), il y aurait également un fort couplage.
Effectivement, ça rejoint un peu les principes d’IoC, mais ce que je trouve intéressant, c’est que ça rejoint aussi les principes de Swing. Avant, je n’avais pas vraiment considéré l’usage des listeners comme intéressant au niveau des dépendances, alors qu’en fait c’est un principe simple mais puissant.
Hello,
merci pour ce retour! :-), c’est très intéressant.
le système de callback est très intéressant… Ça rejoint les principes de l’ioc, non?
Effectivement, l’introspection n’est pas toujours facile à gérer au niveau des dépendances.
Pour ce qui est d’OSGi, il n’y a pas, à ma connaissance, de concepts comme celui que tu cites pour Eclipse.
Dans certains cas les dépendances cycliques peuvent provenir de l’utilisation d’API effectuant de l’introspection ou dépendant d’une configuration présente sur le plugin appelant.
C’est par exemple le cas des plugins contenant la déclaration d’annotations (cas vécu: annotations Jaxb).
Dans ces cas là sous Eclipse RCP le plugin « API » dispose normalement d’un ligne « Eclipse-BuddyPolicy: registered » qui indique qu’il peut accéder aux plugins enregistrés via la ligne « Eclipse-RegisterBuddy: , « .
Est-ce qu’il existe la même chose en OSGI ?
P.S: Si le plugin « API » effectue de l’introspection, le plugin appelant doit pouvoir lui fournir un Classloader valide pour que l’introspection se passe correctement.
Pour ma part, je trouve que la séparation spécification/implémentation très bonne (sans en abuser non plus). Je la met en ouvre dès que je vois plusieurs implémentations possibles.