Quelle stratégie utilisez-vous pour le paquet de nommage en Java projets et pourquoi?
J'ai pensé à cela un certain temps il ya et a récemment refait surface dans ma boutique est en train de faire sa première véritable Java web app.
Comme une intro, je vois deux principaux paquet stratégies de noms. (Pour être clair, je ne suis pas référence à l'ensemble du domaine.de l'entreprise.projet " une partie de cela, je parle de la convention de forfait en dessous.) De toute façon, le paquet de conventions de nommage que je vois sont comme suit:
-
Fonctionnelle: noms des paquets en fonction de leur fonction à l'architecture plutôt que de leur identité en fonction du domaine de l'entreprise. Un autre terme pour ce qui pourrait être d'appellation en fonction de "couche". Donc, vous devez disposer d'un *.package de l'interface utilisateur et un *.de domaine et un package *.orm paquet. Vos paquets sont des tranches horizontales plutôt que verticales.
C'est beaucoup de plus commun que la logique de nommage. En fait, je ne crois pas que j'ai jamais vu ou entendu parler d'un projet qui fait cela. Bien sûr, cela me rend réticent à l'idée (un peu comme penser que vous avez trouvé une solution à un problème NP) que je ne suis pas terriblement intelligent et je suppose que tout le monde doit avoir de très bonnes raisons de le faire de la façon dont ils le font. D'autre part, je ne suis pas opposés à des personnes manquant de peu l'éléphant dans la pièce et je n'ai jamais entendu un argument réel pour faire paquet de nommage de cette façon. Il semble juste être la norme de facto.
-
Logique: noms des paquets en fonction de leur domaine d'affaires de l'identité et de mettre chaque classe qui a à voir avec celle d'une tranche verticale de la fonctionnalité dans ce package.
Je n'ai jamais vu ou entendu parler de cela, comme je l'ai mentionné avant, mais ça a beaucoup de sens pour moi.
-
J'ai tendance à l'approche des systèmes verticalement plutôt qu'horizontalement. Je veux poursuivre et de développer le système de Traitement des commandes, pas la couche d'accès aux données. De toute évidence, il ya une bonne chance que je vais toucher la couche d'accès aux données dans le développement de ce système, mais le point est que je ne pense pas de cette façon. Ce que cela signifie, bien sûr, c'est que lorsque je reçois un ordre de modification ou souhaitez mettre en œuvre certaines nouvelle fonctionnalité, il serait agréable de ne pas avoir à aller à la pêche autour de dans un tas de paquets afin de trouver toutes les classes associées. Au lieu de cela, je viens de regarder dans le X paquet parce que ce que je fais a faire avec X.
-
À partir d'une perspective de développement, je le vois comme une grande victoire d'avoir vos paquets d'un document de votre domaine d'activité, plutôt que de votre architecture. Je me sens comme le domaine est presque toujours la partie du système qui est plus difficile à analyser où, comme l'architecture du système, en particulier en ce point, est en train de devenir banal dans sa mise en œuvre. Le fait que je puisse venir à un système avec ce type de convention de nommage et instantanément à partir de la nomenclature des paquets sais qu'il traite les commandes, les clients, les entreprises, les produits, etc. semble vachement pratique.
-
Il semble que ceci devrait vous permettre de prendre beaucoup mieux avantage de Java est modificateurs d'accès. Cela vous permet de vous beaucoup plus proprement de définir des interfaces en sous-systèmes, plutôt que dans les couches du système. Donc, si vous avez les ordres du sous-système que vous souhaitez de manière transparente persistant, vous pourriez en théorie seulement de ne jamais laisser tout le monde sait que c'est persistante de ne pas avoir à créer des interfaces publiques pour ses classes de persistance dans la couche dao et au lieu de l'emballage de la classe dao avec seulement les classes, qu'il traite. Évidemment, si vous voulu exposer cette fonctionnalité, vous pouvez fournir une interface ou le rendre public. Il semble juste comme vous perdez beaucoup par avoir une tranche verticale de votre système dispose d'répartir sur plusieurs paquets.
-
Je suppose un inconvénient que je vois c'est qu'elle permet de faire l'extraction des couches un peu plus difficile. Au lieu de simplement supprimer ou de renommer un paquet, puis en déposant une nouvelle en place avec une autre technologie, rendez-vous dans et de modifier toutes les classes dans tous les forfaits. Cependant, je ne vois pas ce est une grosse affaire. Il peut être d'un manque d'expérience, mais j'ai imaginer le nombre de fois que vous échangez des technologies n'est rien en comparaison de la quantité de fois que vous accédez et modifiez les tranches verticales à l'intérieur de votre système.
-
Donc je suppose que la question serait alors de sortir de vous, comment faites-vous le nom de vos paquets et pourquoi? S'il vous plaît comprendre que je ne pense pas nécessairement que j'ai trébuché sur la poule aux œufs d'or ou quelque chose ici. Je suis assez nouveau à tout cela, avec la plupart de l'expérience scolaire. Cependant, je ne peux pas repérer les trous dans mon raisonnement, donc je suis en espérant que vous pouvez afin que je puisse passer.
- Ce que j'ai entendu jusqu'à présent, ce qui semble indiquer que c'est un peu un appel de jugement. Cependant, la plupart des réponses n'ont pas porté sur des considérations pratiques. C'est plus ce que je suis à la recherche pour. Merci pour l'aide si loin, si!
- Je ne pense pas que c'est un jugement d'appel. D'abord en divisant par couche, puis par la fonctionnalité est certainement le chemin à parcourir. Je suis à jour de ma réponse.
- dire que pour Eclipse gars 🙂 Dans eclipse, la première division est de "fonctions", qui sont des unités de déploiements et de fonctionnalité. À l'intérieur de "fonctionnalité", il y a des "plugins", qui sont généralement divisés par couche -- mais les plugins dans la même "fonction" doit toujours être déployé à l'ensemble. Si vous avez une seule unité de déploiement, puis en la divisant par la couche d'abord et ensuite seulement par la fonctionnalité peut faire sens. Avec plusieurs unités de déploiement, vous ne voulez pas de simplement déployer couche de présentation -- si vous voulez plus de fonctionnalités.
Vous devez vous connecter pour publier un commentaire.
Pour la conception de l'emballage, j'ai d'abord diviser par couche, puis par d'autres fonctionnalités.
Il y a quelques règles supplémentaires:
Donc, pour une application web par exemple, vous pourriez avoir les couches suivantes au niveau de votre application (de haut en bas):
Pour la suite de la mise en, ces quelques règles supplémentaires:
<prefix.company>.<appname>.<layer>
<root>.<logic>
<root>.private
Voici un exemple de mise en page.
La couche de présentation est divisée en vue de la technologie, et éventuellement par des (groupes de) applications.
De la couche application est divisée en cas d'utilisation.
La couche de service est divisé en domaines d'activité, influencé par le domaine de la logique dans un backend niveau.
L'intégration de la couche est divisée en "technologies" et d'accéder à des objets.
Avantages de la séparation des paquets comme ça, c'est qu'il est plus facile de gérer la complexité, et il augmente la testabilité et la réutilisabilité. Bien qu'il semble comme beaucoup de frais généraux, dans mon expérience, il s'agit en fait de façon très naturelle et tous ceux qui travaillent sur cette structure (ou similaire) ramasse dans une affaire de jours.
Pourquoi dois-je pense que l'approche verticale n'est pas si bon?
Dans le modèle en couches, plusieurs de haut niveau, les modules peuvent utiliser le même niveau inférieur du module. Par exemple: vous pouvez créer plusieurs vues de la même application, plusieurs applications peuvent utiliser le même service, plusieurs services peuvent utiliser la même passerelle. Le truc c'est que lors du déplacement à travers les couches, le niveau de fonctionnalité des changements. Des Modules plus spécifiques couches de ne pas la carte 1-1 sur des modules à partir de la plus générale de la couche, parce que les niveaux de fonctionnalité qu'ils expriment n'est pas la carte 1-1.
Lorsque vous utilisez le vertical approche pour la conception de l'emballage, c'est à dire que vous répartissez par les fonctionnalités d'abord, puis en vigueur tous les blocs de construction de différentes niveaux de fonctionnalité dans la même fonctionnalité veste'. Vous pouvez concevoir vos modules généraux pour le plus spécifique. Mais cela viole le principe important que la plus générale de la couche ne doit pas connaître plus de couches spécifiques. La couche de service par exemple ne devrait pas être modélisé d'après les concepts de la couche d'application.
Je me retrouve coller avec l'Oncle Bob est package principes de conception. En bref, les classes qui doivent être réutilisés ensemble et changé (pour la même raison, par exemple, une dépendance de modification ou d'un changement de structure) doit être mis dans le même paquet. L'OMI, de la ventilation fonctionnelle auraient une meilleure chance d'atteindre ces objectifs que la verticale de l'entreprise rupture dans la plupart des applications.
Par exemple, une tranche horizontale de domaine objets peuvent être réutilisés par différentes sortes de front-ends ou encore des applications et une tranche horizontale de la web front-end est susceptible de se modifier lorsque le sous-jacent framework web qui doit être changé. D'autre part, il est facile d'imaginer l'effet de ces changements dans de nombreux paquets si les classes à travers les différents domaines fonctionnels sont regroupés dans ces paquets.
Évidemment, pas tous les types de logiciels sont les mêmes et le vertical de ventilation peut faire sens (en termes de réalisation des objectifs de réutilisation et de closeability à changer) dans certains projets.
Il y a généralement deux niveaux de la division actuelle. Depuis le sommet, il y a des unités de déploiement. Ceux-ci sont nommés 'logiquement' (pour reprendre vos termes, pensez Eclipse dispose d'). À l'intérieur de déploiement de l'unité, vous avez division fonctionnelle de paquets (pensez à des plugins Eclipse).
Par exemple, la fonctionnalité est
com.feature
, et il se compose decom.feature.client
,com.feature.core
etcom.feature.ui
plugins. À l'intérieur de plugins, j'ai très peu de division à d'autres paquets, même si ce n'est pas inhabituelle trop.Mise à jour: Btw, il ya un grand parler par Juergen Hoeller du code de l'organisation à InfoQ: http://www.infoq.com/presentations/code-organization-large-projects. Juergen est l'un des architectes du Printemps, et sait beaucoup de choses sur ce genre de choses.
La plupart des projets java j'ai travaillé sur la tranche les packages java fonctionnellement d'abord, puis logiquement.
Généralement les pièces sont suffisamment grands, qu'ils sont cassés en deux construire des artefacts, où vous pourriez mettre à la fonctionnalité de base dans un bocal, api dans un autre, interface web des trucs dans un warfile, etc.
Les colis doivent être compilé et distribué à l'unité. Lors de l'examen des classes appartiennent à un package, l'un des principaux critères est ses dépendances. Ce que les autres emballages (y compris les bibliothèques de tiers) cette classe ne dépendent. Un système de cluster classes avec dépendances similaires dans un package. Cela limite l'impact d'un changement dans une bibliothèque, puisque seuls quelques offres bien définies en dépendra.
Il semble que votre logique, à la verticale du système peut avoir tendance à "dénigrement" de dépendances dans la plupart des paquets. C'est-à-dire si chaque fonctionnalité est disponible sous forme d'une tranche verticale, chaque paquet dépendra de chaque tiers de la bibliothèque que vous utilisez. Toute modification d'une bibliothèque est probablement ondulations à travers l'ensemble de votre système.
Personnellement, je préfère le regroupement de classes, logiquement, puis à l'intérieur qui comprend un sous-paquetage pour chaque fonctionnelle de la participation.
Objectifs de l'emballage
Paquets sont après tout sur le groupement des choses ensemble - l'idée étant liées classes vivent à proximité les uns des autres. S'ils vivent dans le même paquet, ils peuvent profiter de colis privé, afin de limiter la visibilité. Le problème, c'est de regrouper tout votre point de vue et persitance des choses dans un seul paquet peut conduire à beaucoup de classes se confondre dans un seul paquet. La prochaine chose la plus sensée à faire est de créer ainsi de la vue, de la persistance, util sous-paquetages et refactoriser classes. Underfortunately protégés et de colis privé de portée ne prend pas en charge le concept de l'ensemble actuel et sous package que ce serait de l'aide dans l'application de ces règles de visibilité.
Je vois maintenant la valeur de la séparation via la fonctionnalité becase quelle valeur est-il de regrouper tous la vue sur les choses liées. Choses dans cette stratégie de nommage déconnecté avec certaines classes de la vue, tandis que d'autres sont dans la persistance et ainsi de suite.
Un exemple de ma logique la structure de l'emballage
Pour les fins de l'illustration permet de donner le nom des deux modules de malades d'utiliser le nom du module en tant que concept que les groupes des classes dans une branche particulière d'un pacckage arbre.
apple.modèle
apple.magasin
la banane.modèle
la banane.magasin
Avantages
Un client à l'aide de la Banane.magasin.BananaStore est seulement exposés à la fonctionnalité que nous souhaitons mettre à la disposition. La version d'hibernate est un détail d'implémentation qui ils n'ont pas besoin d'être au courant et ils ne devraient pas voir ces classes encombrer des opérations de stockage.
D'autres Logiques v avantages Fonctionnels
Le plus haut en direction de la racine de l'ensemble de la portée devient et les choses appartenant à un paquet de commencer à faire preuve de plus de et plus de dépendances à des choses appartenant à d'autres modules. Si l'on examine par exemple la "banane" module de la plupart des dépendances serait limitée à l'intérieur de ce module. En fait, la plupart des aides en vertu de la "banane" ne devrait pas être référencé à l'extérieur de cette étendue d'un package.
Pourquoi la fonctionnalité ?
Quelle est la valeur d'une réaliser par le durcissement des choses en fonction de leur fonctionnalité. La plupart des classes dans ce cas sont indépendants les uns des autres, avec peu ou pas besoin de prendre avantage de colis privé, les méthodes ou classes. Refactoring eux aussi dans leur propre sous-gains peu mais ne aider à réduire l'encombrement.
Développeur des changements dans le système
Quand les développeurs sont chargés de faire des changements qui sont un peu plus que d'anodin il semble idiot que, potentiellement, ils ont des changements qui incluent des fichiers à partir de tous les domaines de l'arbre. Avec la logique de l'approche structurée de leurs changements sont plus local au sein de la même partie de l'arbre qui semble juste à droite.
Les deux fonctionnelle (architecture) et logique (fonction) des approches de l'emballage ont une place. De nombreux exemples d'applications (celles trouvées dans les livres de texte, etc.) suivez l'approche fonctionnelle de la passation de présentation, services d'affaires, de cartographie des données, et d'autres couches architecturales dans des colis séparés. Dans l'exemple des applications, chaque paquet il a souvent un peu ou juste une classe.
Cette première approche est très bien, puisque un exemple artificiel sert souvent à: 1) sur le plan conceptuel de la carte de l'architecture du cadre présenté, 2) est fait avec une logique unique but (par exemple, ajouter/supprimer/mettre à jour/supprimer des animaux de compagnie à partir d'une clinique). Le problème est que beaucoup de lecteurs de prendre cela comme une norme qui n'a pas de limites.
Comme une "entreprise" de l'application s'élargit pour inclure plus de fonctionnalités et plus encore, à la suite de la fonctionnelle approche devient un fardeau. Bien que je sache où chercher pour les types en fonction de l'architecture de la couche (par exemple, web contrôleurs le cadre d'un "web" ou "interface utilisateur" emballage, etc.), l'élaboration d'un seul logique fonction commence à exiger de sauter en arrière et en avant entre de nombreux paquets. C'est gênant, à tout le moins, mais son pire que ça.
Depuis liés logiquement les types ne sont pas réunis, l'API est trop connu du public; l'interaction entre la logique des types est forcé d'être "public", de sorte que les types d'importation et d'interagir les uns avec les autres (la capacité de minimiser à défaut/package de visibilité est perdu).
Si je suis la construction d'un cadre de la bibliothèque, par tous les moyens de mes paquets vont suivre un/fonctionnel architecture emballage d'approche. Mon API, les consommateurs risquent de même apprécier que leurs déclarations d'importation contient intuitive package nommé d'après l'architecture.
À l'inverse, lors de la construction d'une application d'entreprise, je vais paquet par fonctionnalité. Je n'ai pas de problème de placer le Widget de WidgetService, et WidgetController tous dans le même "com.myorg.widget de." package puis en prenant l'avantage de la visibilité par défaut (et d'avoir moins de déclarations d'importation ainsi que les inter-dépendances de package).
Il y a, cependant, cross-over des cas. Si mon WidgetService est utilisé par beaucoup de logique domaines (fonctions), je pourrais créer un "com.myorg.commun.service." package. Il ya aussi une bonne chance que je créer des classes avec l'intention d'être ré-utilisable dans les fonctionnalités et finissent avec des logiciels tels que "com.myorg.commun.l'interface utilisateur.les aides." et "com.myorg.commun.util.". Je peut déplacer tous ces plus tard "commun" des classes dans un projet distinct et de les inclure dans mon application d'entreprise comme un myorg-commons.jar la dépendance.
Il dépend de la granularité de votre processus logiques?
Si ils êtes autonome, vous avez un nouveau projet pour dans le contrôle de source, plutôt que d'un nouveau paquet.
Le projet que je suis en ce moment est égaré vers une logique de fractionnement, il y a un package pour le jython aspect, un package pour un moteur de règles, des packages pour les foo, bar, binglewozzle, etc. Je suis à la recherche d'avoir le XML spécifique analyseurs/écrivains pour chaque module à l'intérieur de ce paquet, plutôt que d'avoir un paquet XML (qui je l'ai fait précédemment), bien qu'il y aura toujours un noyau paquet XML où partagé logique. Une raison à cela cependant, il peut être extensible (plugins) et donc chaque plugin devra également définir son XML (ou de base de données, etc) du code, afin de centraliser les cela pourrait causer des problèmes plus tard.
En fin de compte, il semble être la façon dont elle semble la plus logique pour un projet particulier. Je pense qu'il est facile de paquet le long de la lignes du projet type de couches diagramme cependant. Vous vous retrouverez avec un mélange de logique et fonctionnelle de l'emballage.
Ce qui est nécessaire est balisé espaces de noms. Un analyseur XML pour certains Jython fonctionnalité pourrait être marqués à la fois Jython et XML, plutôt que d'avoir à choisir l'un ou l'autre.
Ou peut-être que je suis wibbling.
Je serais personnellement aller fonctionnelle de nommage. Le court-raison: il évite la duplication de code ou de dépendance cauchemar.
Permettez-moi de préciser un peu. Ce qui se passe quand vous êtes à l'aide d'un fichier jar, avec son propre arbre? Vous êtes effectivement l'importation de l' (compilé) code dans votre projet, et avec elle, un (fonctionnellement séparées) arbre de paquets. Serait-il judicieux d'utiliser les deux conventions de nommage en même temps? Non, à moins que ce qui était caché de vous. Et c'est, si votre projet est assez petit et a un seul composant. Mais si vous avez plusieurs unités logiques, vous ne voulez probablement pas à re-mettre en œuvre, disons, le fichier de données module de chargement. Vous voulez le partager entre les unités logiques, pas artificiel dépendances entre logiquement indépendantes des unités, et ne pas avoir à choisir l'unité dans lequel vous allez mettre cette partagée de l'outil.
Je suppose que c'est pourquoi fonctionnelle de nommage est la plus utilisée dans les projets qui atteignent ou sont censées atteindre une certaine taille, et la logique de nommage est utilisé dans la classe des conventions de nommage pour garder une trace du rôle spécifique, le cas échéant de chaque classe dans un package.
Je vais essayer de répondre plus précisément à chacun de vos points sur la logique de nommage.
Si vous devez aller à la pêche dans les anciennes classes de modifier les fonctionnalités lorsque vous avez un changement de plans, c'est un signe de mauvaise abstraction: vous devez créer des classes qui fournissent une fonctionnalité bien définie, définissable en une courte phrase. Seuls quelques-uns, les classes de niveau supérieur doivent se réunir tous pour refléter votre business intelligence. De cette façon, vous serez en mesure de réutiliser plus de code, avoir un entretien plus facile, plus claire de la documentation et moins de problèmes de dépendance.
Qui dépend principalement de la façon dont vous analyser votre projet. Certainement, logique et du point de vue fonctionnel, sont orthogonaux. Donc, si vous utiliser une convention de nommage, vous avez besoin d'appliquer de l'autre à la classe des noms afin de garder un peu d'ordre, ou à la fourche d'une convention de nommage pour un autre, à une certaine profondeur.
Modificateurs d'accès sont un bon moyen de permettre à d'autres classes qui comprennent vos traitement pour accéder aux entrailles de votre classe. Relation logique ne signifie pas une compréhension de l'algorithmique ou de la simultanéité des contraintes. Fonctionnel, bien qu ' elle ne le fait pas. Je suis très las de modificateurs d'accès autres que publics et privés, parce qu'ils cachent souvent un manque de architecturing et de la classe d'abstraction.
Dans les gros projets commerciaux, l'évolution des technologies qui arrive plus souvent que vous ne le croyez. Par exemple, j'ai dû changer 3 fois déjà de l'analyseur XML, 2 fois de la technologie de mise en cache, et 2 fois de la géolocalisation logiciel. Bonne chose que j'avais cachés, tous les détails dans un kit dédié...
utils
paquet. Ensuite, toutes les classes qui en ont besoin peuvent tout simplement dépendent de ce package.J'essaie de paquet de conception de structures de telle manière que si je devais dessiner un graphe de dépendance, il serait facile à suivre et à utiliser un modèle cohérent, avec aussi peu de références circulaires que possible.
Pour moi, c'est beaucoup plus facile de gérer et de visualiser en un vertical système de nommage plutôt qu'à l'horizontale. si component1.l'affichage a une référence à component2.dataaccess, qui déclenche le plus sonnette d'alarme que si l'affichage.component1 a une référence à dataaccess. component2.
Bien sûr, les composants partagés par les deux vont dans leur propre paquet.
Je suis totalement de suivre et de proposer à la logique ("par fonction") de l'organisation! Un package doit suivre le concept d'un "module" aussi près que possible. L'organisation fonctionnelle peut se propager d'un module sur un projet, il en résulte moins de l'encapsulation, et sujettes à des modifications dans les détails de mise en œuvre.
Prenons un plugin Eclipse par exemple: mettre tous les points de vue ou des actions dans un seul paquet serait un gâchis. Au lieu de cela, chaque composante d'une entité doivent aller à la fonctionnalité du paquet, ou si il y a beaucoup de, en sous-paquets (featureA.les gestionnaires, featureA.préférences, etc.)
Bien sûr, le problème réside dans la hiérarchie de système de paquets (qui, entre autres, Java a), ce qui rend la gestion des orthogonale préoccupations impossible ou au moins très difficile, même si elles se produisent partout!
À partir d'un point de vue purement pratique, java visibilité de constructions permettent à des classes du même package pour accéder aux méthodes et propriétés avec
protected
etdefault
visibilité, ainsi que lapublic
ceux. À l'aide de la non-public des méthodes à partir d'un de complètement différent de la couche de code serait certainement une grosse odeur de code. J'ai donc tendance à placer des classes à partir de la même couche dans le même package.Je n'ai pas souvent l'utilisation de ces protégés ou des méthodes par défaut, d'ailleurs - sauf peut-être dans les tests unitaires de la classe, mais quand je le fais, c'est toujours à partir d'une classe à la même couche
Il dépend. Dans ma ligne de travail, nous avons parfois split paquets par des fonctions (accès aux données, analytique) ou par catégorie d'actif (de crédit, actions, taux d'intérêt). Il suffit de sélectionner la structure qui est la plus commode pour les membres de votre équipe.
C'est une expérience intéressante à ne pas utiliser les paquets (à l'exception de la racine du package.)
La question qui se pose alors, est, quand et pourquoi il est judicieux d'introduire des paquets. Sans doute, la réponse sera différente de ce que vous avez répondu au début du projet.
Je suppose que votre question se pose à tous, car les paquets sont comme les catégories et il est parfois difficile de se décider pour l'une ou l'autre. Parfois, les balises serait plus apprécier à communiquer qu'une classe est utilisable dans de nombreux contextes.
De mon expérience, de la ré-utilisabilité crée plus de problèmes que d'en résoudre. Avec le dernier & bas prix des processeurs et de la mémoire, je préfère la duplication de code plutôt que hermétiquement l'intégration afin de pouvoir les réutiliser.