usar qué proyecto paquetes otro nomenclatura importar funciones declaracion clases java naming-conventions packages

qué - usar un proyecto java en otro



¿Qué estrategia usas para nombrar paquetes en proyectos Java y por qué? (15)

Pensé en esto hace un rato y recientemente resurgió ya que mi tienda está haciendo su primera aplicación web Java real.

Como introducción, veo dos estrategias principales para nombrar paquetes. (Para ser claros, no me estoy refiriendo a la parte completa de ''domain.company.project'' de esto, estoy hablando de la convención del paquete debajo de eso.) De todos modos, las convenciones de nomenclatura de paquetes que veo son las siguientes:

  1. Funcional: Nombrar sus paquetes de acuerdo con su función arquitectónica en lugar de su identidad de acuerdo con el dominio comercial. Otro término para esto podría ser nombrar de acuerdo a ''capa''. Entonces, tendrías un paquete * .ui y un paquete * .dominio y un paquete * .orm. Sus paquetes son rebanadas horizontales en lugar de verticales.

    Esto es mucho más común que la denominación lógica. De hecho, no creo que haya visto o escuchado hablar de un proyecto que hace esto. Esto, por supuesto, me da recelo (algo así como pensar que has encontrado una solución para un problema de NP) ya que no soy muy inteligente y supongo que todos deben tener buenas razones para hacerlo de la manera en que lo hacen. Por otro lado, no me opongo a que la gente solo eche de menos al elefante en la habitación y nunca escuché un argumento real para hacer el nombre del paquete de esta manera. Simplemente parece ser el estándar de facto.

  2. Lógico: Nombrar sus paquetes de acuerdo con su identidad de dominio comercial y colocar cada clase que tenga que ver con esa porción vertical de funcionalidad en ese paquete.

    Nunca he visto o escuchado sobre esto, como mencioné antes, pero tiene muchísimo sentido para mí.

    1. Tiendo a acercarme a los sistemas verticalmente en vez de horizontalmente. Quiero entrar y desarrollar el sistema de procesamiento de pedidos, no la capa de acceso a datos. Obviamente, hay una buena posibilidad de que toque la capa de acceso a datos en el desarrollo de ese sistema, pero el punto es que no lo veo así. Lo que esto significa, por supuesto, es que cuando reciba una orden de cambio o desee implementar alguna característica nueva, sería bueno no tener que ir a pescar en un montón de paquetes para encontrar todas las clases relacionadas. En cambio, solo miro en el paquete X porque lo que estoy haciendo tiene que ver con X.

    2. Desde el punto de vista del desarrollo, considero que es una gran victoria que sus paquetes documenten el dominio de su empresa en lugar de su arquitectura. Siento que el dominio es casi siempre la parte del sistema que es más difícil de asimilar, ya que la arquitectura del sistema, especialmente en este punto, casi se está volviendo mundana en su implementación. El hecho de que pueda acceder a un sistema con este tipo de convención de nomenclatura e instantáneamente a partir de la denominación de los paquetes sabe que se trata de pedidos, clientes, empresas, productos, etc. parece bastante práctico.

    3. Parece que esto le permitiría aprovechar mejor los modificadores de acceso de Java. Esto le permite definir interfaces mucho más limpias en subsistemas en lugar de en capas del sistema. Por lo tanto, si tiene un subsistema de órdenes que desea que sea transparente y persistente, en teoría no podría dejar que otra persona sepa que es persistente al no tener que crear interfaces públicas para sus clases de persistencia en la capa dao y empaquetar la clase dao en con solo las clases que trata. Obviamente, si quisiera exponer esta funcionalidad, podría proporcionarle una interfaz o hacerla pública. Simplemente parece que pierdes mucho de esto al dividir una porción vertical de las características de tu sistema en varios paquetes.

    4. Supongo que una desventaja que puedo ver es que hace que rasgar capas sea un poco más difícil. En lugar de simplemente eliminar o cambiar el nombre de un paquete y luego colocar uno nuevo en su lugar con una tecnología alternativa, debe ingresar y cambiar todas las clases en todos los paquetes. Sin embargo, no veo que esto sea un gran problema. Puede ser por la falta de experiencia, pero tengo que imaginar que la cantidad de veces que intercambias las tecnologías es insignificante en comparación con la cantidad de veces que ingresas y editas las divisiones verticales de funciones dentro de tu sistema.

Así que supongo que la pregunta sería para ti, ¿cómo nombras tus paquetes y por qué? Por favor, comprenda que no creo necesariamente que haya tropezado con el ganso dorado o algo así. Soy bastante nuevo en todo esto, principalmente con experiencia académica. Sin embargo, no puedo encontrar los agujeros en mi razonamiento, así que espero que todos puedan para poder seguir adelante.


¡Sigo y propongo totalmente la organización lógica ("por característica")! Un paquete debe seguir el concepto de un "módulo" lo más cerca posible. La organización funcional puede extender un módulo sobre un proyecto, lo que resulta en menos encapsulamiento y propenso a cambios en los detalles de implementación.

Tomemos un plugin de Eclipse por ejemplo: poner todas las vistas o acciones en un solo paquete sería un desastre. En cambio, cada componente de una característica debe ir al paquete de la característica, o si hay muchos, a los subpaquetes (featureA.handlers, featureA.preferences, etc.)

Por supuesto, el problema radica en el sistema de paquetes jerárquico (que entre otros tiene Java), lo que hace que el manejo de preocupaciones ortogonales sea imposible o al menos muy difícil, ¡aunque ocurren en todas partes!


Depende de la granularidad de tus procesos lógicos?

Si son independientes, a menudo tiene un nuevo proyecto para ellos en control de fuente, en lugar de un paquete nuevo.

El proyecto en el que estoy ahora se está equivocando hacia la división lógica, hay un paquete para el aspecto jython, un paquete para un motor de reglas, paquetes para foo, bar, binglewozzle, etc. Estoy buscando tener los analizadores XML específicos / escritores para cada módulo dentro de ese paquete, en lugar de tener un paquete XML (que he hecho anteriormente), aunque todavía habrá un paquete XML central donde va la lógica compartida. Sin embargo, una razón para esto es que puede ser extensible (complementos) y, por lo tanto, cada complemento necesitará definir también su código XML (o base de datos, etc.), por lo que centralizar esto podría ocasionar problemas más adelante.

Al final, parece ser lo que parece más sensato para el proyecto en particular. Sin embargo, creo que es fácil empaquetar según las líneas del diagrama de capas típico del proyecto. Terminará con una mezcla de empaques lógicos y funcionales.

Lo que se necesita son espacios de nombres etiquetados. Un analizador XML para algunas funcionalidades de Jython podría etiquetarse tanto con Jython como con XML, en lugar de tener que elegir una u otra.

O tal vez estoy bromeando.


Depende. En mi línea de trabajo, a veces dividimos paquetes por funciones (acceso a datos, análisis) o por clase de activos (crédito, acciones, tasas de interés). Simplemente seleccione la estructura que sea más conveniente para su equipo.


Desde un punto de vista puramente práctico, las construcciones de visibilidad de Java permiten que las clases en el mismo paquete accedan a métodos y propiedades con visibilidad protected y default , así como a las public . Usar métodos no públicos desde una capa completamente diferente del código definitivamente sería un gran olor a código. Así que tiendo a poner clases de la misma capa en el mismo paquete.

A menudo no uso estos métodos protegidos o predeterminados en otra parte, excepto posiblemente en las pruebas de unidad para la clase, pero cuando lo hago, siempre es de una clase en la misma capa


Es un experimento interesante no usar paquetes en absoluto (a excepción del paquete raíz).

La pregunta que surge entonces es cuándo y por qué tiene sentido presentar paquetes. Presumiblemente, la respuesta será diferente de la que habría respondido al comienzo del proyecto.

Supongo que su pregunta surge en absoluto, porque los paquetes son como categorías y a veces es difícil decidir por uno u otro. A veces las etiquetas serían más apreciadas para comunicar que una clase es utilizable en muchos contextos.


Intento diseñar estructuras de paquetes de tal manera que si dibujara un gráfico de dependencia, sería fácil de seguir y usar un patrón consistente, con el menor número posible de referencias circulares.

Para mí, esto es mucho más fácil de mantener y visualizar en un sistema de nombres verticales en lugar de horizontal. si component1.display tiene una referencia a component2.dataaccess, arroja más campanas de advertencia que si display.component1 tiene una referencia al acceso a datos. componente2.

Por supuesto, los componentes compartidos por ambos van en su propio paquete.


La mayoría de los proyectos de Java en los que he trabajado cortan los paquetes java funcionalmente primero, luego lógicamente.

Por lo general, las piezas son lo suficientemente grandes como para dividirlas en artefactos de construcción separados, donde puede poner la funcionalidad central en un contenedor, apis en otro, elementos de interfaz web en un archivo warfile, etc.


Los paquetes deben ser compilados y distribuidos como una unidad. Al considerar qué clases pertenecen a un paquete, uno de los criterios clave es sus dependencias. ¿De qué otros paquetes (incluidas las bibliotecas de terceros) depende esta clase? Un sistema bien organizado agrupará clases con dependencias similares en un paquete. Esto limita el impacto de un cambio en una biblioteca, ya que solo unos pocos paquetes bien definidos dependerán de ello.

Parece que su sistema vertical lógico podría tender a "manchar" las dependencias en la mayoría de los paquetes. Es decir, si cada característica se empaqueta como una porción vertical, cada paquete dependerá de cada biblioteca de terceros que use. Cualquier cambio en una biblioteca es probable que se propague a través de todo su sistema.


Me encuentro apegándome a los principios de diseño del paquete del Tío Bob. En resumen, las clases que deben reutilizarse juntas y cambiarse juntas (por la misma razón, por ejemplo, un cambio de dependencia o un cambio de estructura) deben incluirse en el mismo paquete. OMI, el desglose funcional tendría más posibilidades de alcanzar estos objetivos que el desglose vertical / específico del negocio en la mayoría de las aplicaciones.

Por ejemplo, una porción horizontal de objetos de dominio puede ser reutilizada por diferentes tipos de interfaces o incluso aplicaciones, y es probable que una porción horizontal del front-end web cambie conjuntamente cuando se necesite cambiar el marco web subyacente. Por otro lado, es fácil imaginar el efecto dominó de estos cambios en muchos paquetes si las clases en diferentes áreas funcionales se agrupan en esos paquetes.

Obviamente, no todos los tipos de software son iguales y el colapso vertical puede tener sentido (en términos de lograr los objetivos de reutilización y capacidad de cierre) en ciertos proyectos.


Para el diseño del paquete, primero lo divido por capa, luego por alguna otra funcionalidad.

Hay algunas reglas adicionales:

  1. las capas se apilan desde la más general (inferior) hasta la más específica (superior)
  2. cada capa tiene una interfaz pública (abstracción)
  3. una capa solo puede depender de la interfaz pública de otra capa (encapsulación)
  4. una capa solo puede depender de capas más generales (dependencias de arriba a abajo)
  5. una capa preferiblemente depende de la capa directamente debajo de ella

Por lo tanto, para una aplicación web, por ejemplo, podría tener las siguientes capas en su nivel de aplicación (de arriba a abajo):

  • capa de presentación: genera la interfaz de usuario que se mostrará en el nivel del cliente
  • capa de aplicación: contiene lógica que es específica de una aplicación, con estado
  • capa de servicio: agrupa la funcionalidad por dominio, sin estado
  • capa de integración: proporciona acceso al nivel de backend (db, jms, email, ...)

Para el diseño del paquete resultante, estas son algunas reglas adicionales:

  • la raíz de cada nombre de paquete es <prefix.company>.<appname>.<layer>
  • la interfaz de una capa se divide aún más por la funcionalidad: <root>.<logic>
  • la implementación privada de una capa tiene un prefijo privado: <root>.private

Aquí hay un diseño de ejemplo.

La capa de presentación está dividida por la tecnología de visualización y, opcionalmente, por (grupos de) aplicaciones.

com.company.appname.presentation.internal com.company.appname.presentation.springmvc.product com.company.appname.presentation.servlet ...

La capa de aplicación se divide en casos de uso.

com.company.appname.application.lookupproduct com.company.appname.application.internal.lookupproduct com.company.appname.application.editclient com.company.appname.application.internal.editclient ...

La capa de servicio se divide en dominios comerciales, influenciados por la lógica de dominio en un nivel de backend.

com.company.appname.service.clientservice com.company.appname.service.internal.jmsclientservice com.company.appname.service.internal.xmlclientservice com.company.appname.service.productservice ...

La capa de integración se divide en ''tecnologías'' y objetos de acceso.

com.company.appname.integration.jmsgateway com.company.appname.integration.internal.mqjmsgateway com.company.appname.integration.productdao com.company.appname.integration.internal.dbproductdao com.company.appname.integration.internal.mockproductdao ...

Las ventajas de separar paquetes como este es que es más fácil administrar la complejidad y aumenta la capacidad de prueba y la reutilización. Si bien parece una gran cantidad de gastos generales, en mi experiencia, en realidad es muy natural y todos los que trabajan en esta estructura (o similar) lo recogen en cuestión de días.

¿Por qué creo que el enfoque vertical no es tan bueno?

En el modelo en capas, varios módulos de alto nivel diferentes pueden usar el mismo módulo de nivel inferior. Por ejemplo: puede compilar vistas múltiples para la misma aplicación, múltiples aplicaciones pueden usar el mismo servicio, múltiples servicios pueden usar la misma puerta de enlace. El truco aquí es que cuando se mueve a través de las capas, el nivel de funcionalidad cambia. Los módulos en capas más específicas no asignan 1-1 en los módulos de la capa más general, porque los niveles de funcionalidad que expresan no se asignan a 1-1.

Cuando se utiliza el enfoque vertical para el diseño de paquetes, es decir, se divide primero por la funcionalidad, se fuerzan todos los bloques con diferentes niveles de funcionalidad en la misma "chaqueta de funcionalidad". Puede diseñar sus módulos generales para el más específico. Pero esto viola el importante principio de que la capa más general no debería saber sobre capas más específicas. La capa de servicio, por ejemplo, no se debe modelar después de los conceptos de la capa de aplicación.


Personalmente, prefiero agrupar las clases lógicamente, y dentro de eso, incluir un subpaquete para cada participación funcional.

Objetivos de empaque

Los paquetes son, después de todo, sobre agrupar cosas, la idea de que las clases relacionadas estén cerca una de la otra. Si viven en el mismo paquete, pueden aprovechar el paquete privado para limitar la visibilidad. El problema es agrupar toda tu visión y persistencia en un solo paquete, lo que puede llevar a que muchas clases se mezclen en un solo paquete. El próximo paso sensato es crear vistas, persistencia, sub paquetes de paquetes y clases de refactor en consecuencia. Por desgracia, el alcance privado protegido y empaquetado no es compatible con el concepto del paquete y subpaquete actual, ya que esto ayudaría a hacer cumplir dichas reglas de visibilidad.

Ahora veo valor en la separación a través de la funcionalidad, ya que el valor está ahí para agrupar todo lo relacionado con la vista. Las cosas en esta estrategia de nombres se desconectan con algunas clases en la vista, mientras que otras están en persistencia y así sucesivamente.

Un ejemplo de mi estructura de empaque lógico

Para propósitos de ilustración, nombre dos módulos - use el módulo de nombre como un concepto que agrupa clases bajo una rama particular de un árbol de pacckage.

apple.model apple.store banana.model banana.store

Ventajas

Un cliente que utiliza el Banana.store.BananaStore solo está expuesto a la funcionalidad que deseamos poner a disposición. La versión de hibernación es un detalle de implementación del que no necesitan estar al tanto ni deben ver estas clases ya que agregan desorden a las operaciones de almacenamiento.

Otras ventajas lógicas de Functional v

Cuanto más se asciende hacia la raíz, más amplio se vuelve el alcance y las cosas que pertenecen a un paquete comienzan a exhibir más y más dependencias en cosas que pertenecen a otros módulos. Si se examinara, por ejemplo, el módulo "banana", la mayoría de las dependencias se limitarían a ese módulo. De hecho, la mayoría de los ayudantes bajo "banana" no se mencionarán en absoluto fuera del alcance de este paquete.

¿Por qué funcionalidad?

¿Qué valor se logra agrupando las cosas en función de la funcionalidad? La mayoría de las clases en tal caso son independientes entre sí con poca o ninguna necesidad de aprovechar los paquetes de métodos privados o clases. Al refaccionarlos en sus propios subpaquetes se gana poco pero ayuda a reducir el desorden.

Desarrollador cambia al sistema

Cuando los desarrolladores tienen la tarea de realizar cambios que son un poco más que triviales, parece una tontería que potencialmente tengan cambios que incluyen archivos de todas las áreas del árbol de paquetes. Con el enfoque lógico estructurado, sus cambios son más locales dentro de la misma parte del árbol de paquetes que parece correcto.


Por lo general, hay dos niveles de división presentes. Desde arriba, hay unidades de despliegue. Estos se denominan ''lógicamente'' (en sus términos, piense en las características de Eclipse). Dentro de la unidad de despliegue, tiene una división funcional de paquetes (piense en los plugins de Eclipse).

Por ejemplo, la función es com.feature y consta de los com.feature.client , com.feature.core y com.feature.ui . Complementos internos, tengo muy poca división con otros paquetes, aunque eso no es inusual también.

Actualización: Por cierto, hay una gran charla de Juergen Hoeller sobre la organización del código en InfoQ: http://www.infoq.com/presentations/code-organization-large-projects . Juergen es uno de los arquitectos de Spring, y sabe mucho sobre esto.


Según mi experiencia, la reutilización crea más problemas que la resolución. Con los procesadores y la memoria más recientes y económicos, preferiría la duplicación de código en lugar de una integración estricta para volver a utilizar.


Tanto los enfoques funcionales (arquitectónicos) como lógicos (de funciones) tienen un lugar. Muchas aplicaciones de ejemplo (las que se encuentran en los libros de texto, etc.) siguen el enfoque funcional de colocar la presentación, los servicios comerciales, la asignación de datos y otras capas arquitectónicas en paquetes separados. En aplicaciones de ejemplo, cada paquete a menudo tiene solo unas pocas o solo una clase.

Este enfoque inicial está bien, ya que un ejemplo artificial a menudo sirve para: 1) diseñar conceptualmente la arquitectura del marco presentado, 2) se hace con un único propósito lógico (por ejemplo, agregar / eliminar / actualizar / eliminar mascotas de una clínica) . El problema es que muchos lectores toman esto como un estándar que no tiene límites.

A medida que una aplicación "comercial" se expande para incluir más y más características, seguir el enfoque funcional se convierte en una carga. Aunque sé dónde buscar tipos basados ​​en la capa de arquitectura (por ejemplo, controladores web en un paquete "web" o "ui", etc.), desarrollar una única característica lógica comienza a requerir saltar de un lado a otro entre muchos paquetes. Esto es engorroso, al menos, pero es peor que eso.

Como los tipos lógicamente relacionados no se empaquetan juntos, la API se publicita demasiado; la interacción entre los tipos lógicamente relacionados se ve obligada a ser "pública" para que los tipos puedan importar e interactuar entre sí (se pierde la capacidad de minimizar la visibilidad predeterminada / del paquete).

Si estoy construyendo una biblioteca de frameworks, mis paquetes seguirán un enfoque de empaquetado funcional / arquitectónico. Los consumidores de mi API incluso pueden apreciar que sus declaraciones de importación contienen un paquete intuitivo que lleva el nombre de la arquitectura.

Por el contrario, cuando construyas una aplicación comercial, empaquetaré por característica. No tengo problemas para colocar Widget, WidgetService y WidgetController en el mismo paquete " com.myorg.widget. " Y luego aprovechar la visibilidad predeterminada (y tener menos declaraciones de importación y dependencias entre paquetes).

Sin embargo, hay casos cruzados. Si muchos dominios lógicos (funciones) usan mi WidgetService, podría crear un paquete " com.myorg.common.service. " También existe una buena posibilidad de que cree clases con la intención de volver a utilizarlas en todas las funciones y terminar con paquetes como " com.myorg.common.ui.helpers " y " com.myorg.common.util ". Incluso puedo terminar moviendo todas estas clases "comunes" posteriores en un proyecto separado e incluirlas en mi aplicación comercial como una dependencia de myorg-commons.jar.


Yo personalmente iría por nombres funcionales. El breve motivo: evita la duplicación de código o la pesadilla de dependencia.

Déjame elaborar un poco. ¿Qué sucede cuando está utilizando un archivo jar externo, con su propio árbol de paquetes? Está efectivamente importando el código (compilado) en su proyecto y con él un árbol de paquetes (funcionalmente separado). ¿Tendría sentido usar las dos convenciones de nombres al mismo tiempo? No, a menos que eso esté oculto para ti. Y lo es, si su proyecto es lo suficientemente pequeño y tiene un solo componente. Pero si tiene varias unidades lógicas, probablemente no desee volver a implementar, digamos, el módulo de carga del archivo de datos. Desea compartirlo entre unidades lógicas, no tener dependencias artificiales entre unidades lógicamente no relacionadas, y no tener que elegir en qué unidad va a poner esa herramienta compartida en particular.

Supongo que esta es la razón por la que la denominación funcional es la más utilizada en proyectos que alcanzan, o están destinados a alcanzar, un cierto tamaño, y el uso de nombres lógicos en las convenciones de nomenclatura de la clase para realizar un seguimiento del rol específico, si corresponde. paquete.

Trataré de responder con más precisión a cada uno de sus puntos sobre nombres lógicos.

  1. Si tiene que ir a pescar en clases antiguas para modificar las funcionalidades cuando tiene un cambio de planes, es un signo de mala abstracción: debe crear clases que proporcionen una funcionalidad bien definida, definible en una frase corta. Solo unas pocas clases de alto nivel deben ensamblar todo esto para reflejar su inteligencia empresarial. De esta forma, podrá reutilizar más código, tener un mantenimiento más sencillo, una documentación más clara y menos problemas de dependencia.

  2. Eso depende principalmente de la forma en que asimile su proyecto. Definitivamente, la vista lógica y funcional es ortogonal. Por lo tanto, si utiliza una convención de nomenclatura, debe aplicar la otra a los nombres de clase para mantener un cierto orden, o pasar de una convención de nomenclatura a otra con cierta profundidad.

  3. Los modificadores de acceso son una buena manera de permitir que otras clases que entienden su procesamiento accedan a las entrañas de su clase. La relación lógica no significa una comprensión de las restricciones algorítmicas o de concurrencia. Funcional puede, aunque no lo hace. Estoy muy cansado de los modificadores de acceso que no sean públicos y privados, ya que a menudo esconden la falta de una adecuada arquitectura y abstracción de clases.

  4. En grandes proyectos comerciales, las tecnologías cambiantes ocurren con más frecuencia de lo que usted cree. Por ejemplo, tuve que cambiar 3 veces el analizador XML, 2 veces la tecnología de almacenamiento en caché y 2 veces el software de geolocalización. Menos mal que había escondido todos los detalles en un paquete dedicado ...