web-dev-qa-db-fra.com

L'approche package par fonctionnalité est-elle bonne?

Récemment, je suis tombé sur ce post javalobby http://Java.dzone.com/articles/how-changing-Java-package sur l'emballage Java code par fonctionnalité.

J'aime l'idée, mais j'ai quelques questions sur cette approche. J'ai posé ma question mais je n'ai pas obtenu de réponse satisfaisante. J'espère que quelqu'un sur StackOverflow pourra clarifier mes questions.

J'aime l'idée de package par fonctionnalité qui réduit considérablement le temps de déplacement entre les packages lors du codage et toutes les informations associées seront au même endroit (package). Mais qu'en est-il des interactions entre les services dans différents packages?

Supposons que nous construisons une application de blog et que nous mettons toutes les opérations liées aux utilisateurs (contrôleurs/services/référentiels) dans le package com.mycompany.myblog.users. Et toutes les opérations liées aux articles de blog (contrôleurs/services/référentiels) dans le package com.mycompany.myblog.posts.

Maintenant, je veux montrer le profil utilisateur avec tous les messages qu'il a postés. Dois-je appeler myblog.posts.PostsService.getPostsByUser(userId) depuis myblog.users.UserController.showUserProfile()?

Qu'en est-il du couplage entre les packages?

Aussi, partout où je lis sur le package par fonctionnalité, tout le monde dit que c'est une bonne pratique. Alors pourquoi de nombreux auteurs de livres et même des frameworks encouragent-ils à se regrouper par couches? Juste curieux de savoir :-)

51

Jetez un oeil à l'oncle Bob's Package Design Principles . Il explique les raisons et les motivations de ces principes, que j'ai développés ci-dessous:

Les classes qui sont réutilisées ensemble doivent être emballées ensemble afin que l'emballage puisse être traité comme une sorte de produit complet disponible pour vous. Et ceux qui sont réutilisés ensemble doivent être séparés de ceux avec lesquels ils ne sont pas réutilisés. Par exemple, vos classes utilitaires de journalisation ne sont pas nécessairement utilisées avec vos classes de fichiers io. Emballez donc tous les journaux séparément. Mais les classes de journalisation peuvent être liées les unes aux autres. Donc, créez une sorte de produit complet pour la journalisation, disons, à défaut d'un meilleur nom commun-logging package dans un pot (ré) utilisable et un autre produit complet séparé pour les utilitaires io, encore une fois à défaut d'un meilleur nom, disons commun - io.jar. Si vous mettez à jour la bibliothèque say commons-io pour dire support Java nio, vous ne voudrez pas nécessairement apporter de modifications à la bibliothèque de journalisation. Il est donc préférable de les séparer.

Supposons maintenant que vous vouliez que vos classes d'utilitaires de journalisation prennent en charge la journalisation structurée, par exemple une sorte d'analyse de journal par des outils tels que Splunk. Certains clients de votre utilitaire de journalisation peuvent souhaiter effectuer une mise à jour vers votre nouvelle version; d'autres non. Ainsi, lorsque vous publiez une nouvelle version, empaquetez toutes les classes nécessaires et réutilisées ensemble pour la migration. Ainsi, certains clients de vos classes utilitaires peuvent supprimer en toute sécurité votre ancien pot de journalisation des communs et passer au nouveau pot de commons-journalisation. Certains autres clients sont toujours d'accord avec des pots plus anciens. Cependant, aucun client n'est nécessaire pour avoir ces deux jars (nouveaux et anciens) simplement parce que vous les avez forcés à utiliser certaines classes pour les jars emballés plus anciens.

Évitez les dépendances cycliques. a dépend de b; b sur c; c sur d; mais d dépend de a. Le scénario est évidemment dissuasif car il sera très difficile de définir des couches ou des modules, etc. et vous ne pourrez pas les faire varier indépendamment les uns des autres.

De plus, vous pouvez empaqueter vos classes de telle sorte que si une couche ou un module change, d'autres modules ou couches ne doivent pas nécessairement changer. Ainsi, par exemple, si vous décidez de passer de l'ancien framework MVC à une mise à niveau des API de repos, seuls la vue et le contrôleur peuvent avoir besoin de modifications; votre modèle ne fonctionne pas.

25
Atul

Il y a beaucoup d'autres aspects que le couplage pour la conception de paquets, je suggère de regarder les principes OOAD, en particulier les principes de conception de paquets comme

REP Le principe d'équivalence de réutilisation des versions Le granule de réutilisation est le granule de libération.

CCP Le principe commun de fermeture Les classes qui changent ensemble sont regroupées.

CRP Le principe de réutilisation commun Les classes utilisées ensemble sont regroupées.

ADP Le principe des dépendances acycliques Le graphe de dépendance des packages ne doit pas avoir de cycles.

SDP Le principe des dépendances stables Dépend du sens de la stabilité.

SAP Le principe des abstractions stables L'abstraction augmente avec la stabilité.

pour plus d'informations, vous pouvez lire livre "Développement de logiciel agile, principes, modèles et pratiques"

14
Jigar Parekh

Personnellement, j'aime l'approche "package par fonctionnalité", même si vous devez faire preuve de beaucoup de jugement sur l'endroit où tracer les limites du package. C'est certainement une approche réalisable et sensée dans de nombreuses circonstances.

Vous devriez probablement réaliser le couplage entre les packages et les modules à l'aide d'interfaces publiques - cela maintient le couplage propre et gérable.

Il est parfaitement normal que le package "articles de blog" appelle le package "utilisateurs" tant qu'il utilise des interfaces publiques bien conçues pour le faire.

Un gros conseil si vous suivez cette approche: soyez très attentif à vos dépendances et en particulier évitez les dépendances circulaires entre les packages. Une bonne conception devrait ressembler à un arbre de dépendance - avec les zones de fonctionnalité de niveau supérieur dépendant d'un ensemble de services communs qui dépendent de bibliothèques de fonctions utilitaires, etc. Dans une certaine mesure, cela commencera à ressembler à des "couches" architecturales avec fin des packages appelant des services principaux.

12
mikera