¿Qué estrategia utiliza para nombrar paquetes en proyectos Java y por qué? [cerrado]

88

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

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

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

    Esto es mucho más común que la denominación lógica. De hecho, no creo haber visto ni oído hablar de un proyecto que haga esto. Por supuesto, esto me hace receloso (algo así como pensar que has encontrado una solución a un problema de NP), ya que no soy muy inteligente y supongo que todos deben tener buenas razones para hacerlo de la forma en que lo hacen. Por otro lado, no me opongo a que la gente simplemente extrañe al elefante en la habitación y nunca escuché un argumento real para nombrar paquetes 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 poner cada clase que tiene que ver con esa porción vertical de funcionalidad en ese paquete.

    Nunca había visto ni oído hablar de esto, como mencioné antes, pero tiene mucho sentido para mí.

    1. Tiendo a acercarme a los sistemas verticalmente en lugar 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 de esa manera. Lo que esto significa, por supuesto, es que cuando recibo una orden de cambio o quiero implementar alguna característica nueva, sería bueno no tener que andar buscando 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, veo como una gran ventaja que sus paquetes documenten el dominio de su negocio 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, se está volviendo casi mundana en su implementación. El hecho de que pueda llegar a un sistema con este tipo de convención de nomenclatura e instantáneamente desde la nomenclatura de los paquetes saber que se trata de pedidos, clientes, empresas, productos, etc., parece bastante útil.

    3. Parece que esto le permitiría aprovechar mucho mejor los modificadores de acceso de Java. Esto le permite definir interfaces de manera mucho más limpia en subsistemas que en capas del sistema. Entonces, si tiene un subsistema de pedidos que desea que sea persistente de manera transparente, en teoría, nunca podría dejar que nadie sepa que es persistente al no tener que crear interfaces públicas para sus clases de persistencia en la capa dao y, en su lugar, empaquetar la clase dao en sólo con las clases de las que trata. Obviamente, si desea exponer esta funcionalidad, puede proporcionar una interfaz para ella o hacerla pública. Parece que pierde mucho de esto al dividir una porción vertical de las funciones de su sistema en varios paquetes.

    4. Supongo que una desventaja que puedo ver es que hace que arrancar 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 gran cosa. Puede ser por falta de experiencia, pero tengo que imaginar que la cantidad de veces que intercambia tecnologías palidece en comparación con la cantidad de veces que ingresa y edita segmentos de características verticales dentro de su sistema.

Entonces, supongo que la pregunta se le haría a usted, ¿cómo nombran sus paquetes y por qué? Por favor, comprenda que no creo necesariamente que me haya topado con la gallina de los huevos de oro o algo así. Soy bastante nuevo en todo esto y tengo mayormente experiencia académica. Sin embargo, no puedo detectar los agujeros en mi razonamiento, así que espero que todos puedan para que yo pueda seguir adelante.

Tim Visher
fuente
Lo que he escuchado hasta ahora parece indicar que es una especie de decisión. Sin embargo, la mayoría de las respuestas no se han centrado en consideraciones prácticas. Eso es más lo que estoy buscando. ¡Gracias por la ayuda hasta ahora!
Tim Visher
No creo que sea una decisión de criterio. Primero dividir por capa, luego por funcionalidad es definitivamente el camino a seguir. Estoy actualizando mi respuesta.
eljenso
@eljenso: dígaselo a los chicos de Eclipse :) En eclipse, la primera división es para "características", que son unidades de implementación y también funcionalidad. Dentro de "característica", hay "complementos", que generalmente se dividen por capas, pero los complementos dentro de la misma "característica" siempre deben implementarse juntos. Si solo tiene una unidad de implementación, entonces dividir primero por capa y solo luego por funcionalidad puede tener sentido. Con varias unidades de implementación, no desea implementar solo la capa de presentación, desea una pieza adicional de funcionalidad.
Peter Štibraný
Tengo una pregunta específica sobre la capa empresarial de una aplicación, ¿cuál debería ser la convención de nomenclatura?
Bionix1441

Respuestas:

32

Para el diseño de paquetes, 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) a 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

Entonces, 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 de 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, correo electrónico, ...)

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 funcionalidad: <root>.<logic>
  • la implementación privada de una capa tiene el prefijo privado: <root>.private

Aquí hay un diseño de ejemplo.

La capa de presentación está dividida por tecnología de vista 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, influenciada por la lógica del 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 captan 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 utilizar el mismo módulo de nivel inferior. Por ejemplo: puede crear varias vistas para la misma aplicación, varias aplicaciones pueden utilizar el mismo servicio, varios servicios pueden utilizar la misma puerta de enlace. El truco aquí es que al moverse a través de las capas, el nivel de funcionalidad cambia. Los módulos en capas más específicas no se asignan 1-1 en módulos de la capa más general, porque los niveles de funcionalidad que expresan no se asignan 1-1.

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

eljenso
fuente
"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". Realmente no entras en las razones por las que esto es así.
mangoDrunk
1
No entras en las razones por las que no es así. Pero de todos modos ... las reglas de organización son bastante claras y sencillas, por lo que reduce la complejidad. Es más comprobable y reutilizable debido a la propia organización. Todos pueden probarlo y después podemos estar de acuerdo o en desacuerdo. Explico brevemente algunas de las desventajas del enfoque vertical, dando implícitamente razones por las que creo que el enfoque horizontal es más fácil.
eljenso
8
Este artículo explica muy bien por qué es mejor usar paquete por función en lugar de paquete por capa .
Tom
@eljenso "No entras en las razones por las que no es así", ¿tratando de desviar la carga de la prueba? Creo que el artículo publicado por Tom explica muy bien por qué es mejor el paquete por función y por qué me cuesta aceptar "No creo que sea una decisión de criterio. Primero dividir por capa, luego por funcionalidad es definitivamente la forma de ir "en serio.
pka
18

Me apego a los principios de diseño de paquetes 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 marco) deben colocarse en el mismo paquete. En mi opinión, el desglose funcional tendría más posibilidades de lograr estos objetivos que el desglose vertical / específico de la empresa en la mayoría de las aplicaciones.

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

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

Buu Nguyen
fuente
Gracias, esto es muy claro.Como dije en la pregunta, siento que la cantidad de veces que cambiaría tecnologías es mucho menor de lo que cambiaría por segmentos verticales de funcionalidad. ¿No ha sido esta tu experiencia?
Tim Visher
No se trata solo de tecnologías. El punto n. ° 1 de su publicación original solo tiene sentido si las secciones verticales son aplicaciones / servicios independientes que se comunican entre sí a través de alguna interfaz (SOA, si lo desea). más abajo ...
Buu Nguyen
Ahora, a medida que avanza en los detalles de cada una de esas aplicaciones / servicios detallados que tienen su propia interfaz gráfica de usuario / negocios / datos, difícilmente puedo imaginar que los cambios en un segmento vertical, ya sea sobre tecnología, dependencia, reglas / flujo de trabajo, registro , seguridad, estilo de interfaz de usuario, se puede aislar completamente de otras secciones.
Buu Nguyen
Me interesaría sus comentarios sobre mi respuesta
i3ensays
@BuuNguyen El enlace a los principios de diseño del paquete está roto.
johnnieb
5

Suelen estar presentes ambos niveles de división. Desde arriba, hay unidades de despliegue. Estos se nombran 'lógicamente' (en sus términos, piense en las características de Eclipse). Dentro de la unidad de implementación, tiene una división funcional de paquetes (piense en los complementos de Eclipse).

Por ejemplo, la característica es com.feature, y consta de com.feature.client, com.feature.corey com.feature.uicomplementos. Dentro de los complementos, tengo muy poca división con otros paquetes, aunque eso tampoco es inusual.

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 este tema.

Peter Štibraný
fuente
No lo sigo del todo. Por lo general, es posible que vea com.apache.wicket.x donde x es funcional o lógico. Normalmente no veo com.x. Supongo que estás diciendo que irías con una estructura com.company.project.feature.layer. Tienes razones?
Tim Visher
La razón es que "com.company.project.feature" es la unidad de implementación. En este nivel, algunas funciones son opcionales y se pueden omitir (es decir, no implementar). Sin embargo, dentro de la función, las cosas no son opcionales y, por lo general, las quiere todas. Aquí tiene más sentido dividir por capas.
Peter Štibraný
4

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

Por lo general, las partes son lo suficientemente grandes como para dividirlas en artefactos de compilación separados, donde puede colocar la funcionalidad principal en un jar, las apis en otro, las cosas de la interfaz web en un archivo de guerra, etc.

Ben Hardy
fuente
¿A que podría parecerse? domain.company.project.function.logicalSlice?
Tim Visher
bastante! egdcpweb.profiles, dcpweb.registration, dcpapis, dcppersistence etc. generalmente funcionan bastante bien, su kilometraje puede variar. depende también de si está utilizando el modelado de dominios; si tiene varios dominios, es posible que desee dividirlos por dominio primero.
Ben Hardy
Yo personalmente prefiero lo contrario, lógico que funcional. manzanas.rpc, manzanas.modelo y luego banana.modelo, banana.store
mP.
Es más valioso poder agrupar todas las cosas de Apple juntas que agrupar todas las cosas de la web.
mP.
3

Los paquetes deben compilarse y distribuirse como una unidad. Al considerar qué clases pertenecen a un paquete, uno de los criterios clave son 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 ella.

Parece que su sistema vertical y lógico podría tender a "manchar" las dependencias en la mayoría de los paquetes. Es decir, si cada característica está empaquetada como un segmento vertical, cada paquete dependerá de cada biblioteca de terceros que utilice. Es probable que cualquier cambio en una biblioteca se propague por todo el sistema.

erickson
fuente
¡Ah! Entonces, una cosa que hace hacer cortes horizontales es protegerlo de las actualizaciones reales en las bibliotecas que está utilizando. Creo que tiene razón en que los cortes verticales borran todas las dependencias que tiene su sistema en todos los paquetes. ¿Por qué es esto tan importante?
Tim Visher
Para una "característica", no es tan importante. Suelen ser más volátiles y, de todos modos, tienen más dependencias. Por otro lado, este código de "cliente" tiende a tener una baja reutilización. Para algo que pretende ser una biblioteca reutilizable, aislar a los clientes de cada pequeño cambio es una gran inversión.
erickson
3

Personalmente, prefiero agrupar las clases de manera lógica y luego incluir un subpaquete para cada participación funcional.

Objetivos del embalaje

Después de todo, los paquetes se tratan de agrupar cosas: la idea de que las clases relacionadas vivan cerca unas de otras. Si viven en el mismo paquete, pueden aprovechar el paquete privado para limitar la visibilidad. El problema es que agrupar todas las cosas de vista y persistencia en un paquete puede llevar a que muchas clases se mezclen en un solo paquete. Lo siguiente que se puede hacer es crear vista, persistencia, subpaquetes util y clases de refactorización en consecuencia. Desafortunadamente, el alcance privado del paquete y protegido no admite 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 porque ¿qué valor hay para agrupar todas las cosas relacionadas con la vista? Las cosas en esta estrategia de nomenclatura se desconectan con algunas clases en la vista mientras que otras permanecen en persistencia y así sucesivamente.

Un ejemplo de mi estructura lógica de empaque

Para fines de ilustración, nombremos dos módulos; usaré el módulo de nombres como un concepto que agrupa clases bajo una rama particular de un árbol de paquetes.

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

Ventajas

Un cliente que utiliza 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 que no necesitan conocer ni deben ver estas clases, ya que agregan desorden a las operaciones de almacenamiento.

Otras ventajas lógicas v funcionales

Cuanto más 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 de las cosas que pertenecen a otros módulos. Si uno 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 serían referenciados en absoluto fuera del alcance de este paquete.

¿Por qué funcionalidad?

¿Qué valor se logra al agrupar 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 métodos o clases privados del paquete. Refactorizarlos en sus propios subpaquetes 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 incluyan archivos de todas las áreas del árbol de paquetes. Con el enfoque estructurado lógico, sus cambios son más locales dentro de la misma parte del árbol de paquetes, lo que parece correcto.

mP.
fuente
"Cuando los desarrolladores [...] archivos de todas las áreas del árbol de paquetes". Tiene razón cuando dice que esto parece una tontería. Porque este es exactamente el punto de la estratificación adecuada: los cambios no afectan a toda la estructura de la aplicación.
eljenso
Gracias por el consejo. No estoy seguro de entenderlo claramente. Creo que está tratando de defender los paquetes lógicos, no tengo muy claro por qué. ¿Podría intentar hacer su respuesta un poco más clara, posiblemente reformulándola? ¡Gracias!
Tim Visher
3

Ambos enfoques funcionales (arquitectónicos) y lógicos (características) del empaque tienen un lugar. Muchas aplicaciones de ejemplo (las que se encuentran en libros de texto, etc.) siguen el enfoque funcional de colocar presentaciones, servicios comerciales, mapeo 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) trazar un mapa conceptual de la arquitectura del marco que se presenta, 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 "empresarial" se expande para incluir más y más funciones, 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.), el desarrollo de una única característica lógica comienza a requerir saltar de un lado a otro entre muchos paquetes. Esto es engorroso, como mínimo, pero es peor que eso.

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

Si estoy construyendo una biblioteca marco, por supuesto, mis paquetes seguirán un enfoque de empaquetado funcional / arquitectónico. Mis consumidores de API podrían incluso apreciar que sus declaraciones de importación contienen un paquete intuitivo con el nombre de la arquitectura.

Por el contrario, al crear una aplicación empresarial, la empaquetaré por función. No tengo ningún problema 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 así como dependencias entre paquetes).

Sin embargo, existen casos cruzados. Si mi WidgetService es utilizado por muchos dominios lógicos (características), podría crear un paquete " com.myorg.common.service. " También hay una buena posibilidad de que cree clases con la intención de que sean reutilizables en todas las funciones y termine 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 myorg-commons.jar.

i3ensays
fuente
2

¿Depende de la granularidad de sus procesos lógicos?

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

El proyecto en el que estoy en este momento 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 analizadores XML específicos / escritores para cada módulo dentro de ese paquete, en lugar de tener un paquete XML (lo 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 centralizarlo podría presentar 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 de empaquetar siguiendo las líneas del diagrama en capas de un proyecto típico. Terminará con una combinación de empaque lógico y funcional.

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

O tal vez estoy temblando.

JeeBee
fuente
No entiendo completamente tu punto. Creo que lo que estás diciendo es que debes hacer lo que tenga más sentido para tu proyecto y para ti eso es lógico con un poco de funcionalidad. ¿Hay razones prácticas específicas para esto?
Tim Visher
Como dije, tendré complementos para aumentar la funcionalidad incorporada. Un complemento tendrá que poder analizar y escribir su XML (y eventualmente en la base de datos), así como proporcionar funcionalidad. Por lo tanto, ¿tengo com.xx.feature.xml o com.xx.xml.feature? El primero parece más ordenado.
JeeBee
2

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

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

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

levand
fuente
Según tengo entendido entonces, abogaría por la convención de nomenclatura vertical. Creo que para esto es un paquete * .utils, cuando necesitas una clase en varios segmentos.
Tim Visher
2

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

Tomemos un complemento de Eclipse, por ejemplo: poner todas las vistas o acciones en un 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, en subpaquetes (featureA.handlers, featureA.preferences, etc.)

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

thSoft
fuente
1

Personalmente, optaría por nombres funcionales. La razón corta: evita la duplicación de código o la pesadilla de dependencia.

Déjame desarrollar un poco. ¿Qué sucede cuando está utilizando un archivo jar externo, con su propio árbol de paquetes? Está importando efectivamente el código (compilado) a su proyecto, y con él un árbol de paquetes (separado funcionalmente). ¿Tendría sentido utilizar las dos convenciones de nomenclatura al mismo tiempo? No, a menos que se lo oculte. 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 de archivos 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 colocar 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 la denominación lógica se utiliza en las convenciones de denominación de clases para realizar un seguimiento del rol específico, si es que hay alguno de cada clase en un paquete.

Intentaré responder con mayor precisión a cada uno de sus puntos sobre la denominación lógica.

  1. Si tienes que ir a pescar en clases antiguas para modificar funcionalidades cuando tienes un cambio de planes, es señal de mala abstracción: debes construir clases que brinden una funcionalidad bien definida, definible en una frase corta. Solo unas pocas clases de alto nivel deberían reunir todo esto para reflejar su inteligencia empresarial. De esta manera, podrá reutilizar más código, tener un mantenimiento más fácil, 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. Entonces, si usa una convención de nomenclatura, debe aplicar la otra a los nombres de las clases para mantener algún orden, o pasar de una convención de nomenclatura a otra con cierta profundidad.

  3. Los modificadores de acceso son una buena forma de permitir que otras clases comprendan 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, porque a menudo ocultan una falta de una arquitectura adecuada y una abstracción de clases.

  4. En grandes proyectos comerciales, el cambio de tecnologías ocurre con más frecuencia de lo que cree. Por ejemplo, ya he tenido 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 ...

Varkhan
fuente
1
Perdóname si me equivoco, pero me parece que estás hablando más de desarrollar un marco que esté destinado a ser utilizado por muchas personas. Creo que las reglas cambian entre ese tipo de desarrollo y el desarrollo de sistemas de usuario final. ¿Me equivoco?
Tim Visher
Creo que para las clases comunes utilizadas por muchos de los cortes verticales, tiene sentido tener algo como un utilspaquete. Entonces, cualquier clase que lo necesite simplemente puede depender de ese paquete.
Tim Visher
Una vez más, el tamaño importa: cuando un proyecto se vuelve lo suficientemente grande, necesita ser apoyado por varias personas y se producirá algún tipo de especialización. Para facilitar la interacción y evitar errores, la segmentación del proyecto en partes se vuelve rápidamente necesaria. ¡Y el paquete utils pronto se volverá descomunal!
Varkhan
1

Es un experimento interesante no usar paquetes en absoluto (excepto el paquete raíz).

La pregunta que surge entonces es cuándo y por qué tiene sentido introducir paquetes. Presumiblemente, la respuesta será diferente a 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 se puede usar en muchos contextos.

user53682
fuente
0

Desde un punto de vista puramente práctico, construcciones de visibilidad de Java permiten a las clases en el mismo paquete a métodos de acceso y con propiedades protectedy defaultvisibilidad, así como lapublic queridos. Usar métodos no públicos de una capa completamente diferente del código definitivamente sería un gran olor a código. Por eso tiendo a poner clases de la misma capa en el mismo paquete.

No suelo usar estos métodos protegidos o predeterminados en otros lugares, excepto posiblemente en las pruebas unitarias para la clase, pero cuando lo hago, siempre es de una clase en la misma capa

Bill Michell
fuente
¿No es algo de la naturaleza de los sistemas de múltiples capas depender siempre de la siguiente capa? Por ejemplo, la interfaz de usuario depende de su capa de servicios que depende de su capa de dominio, etc. Empaquetar porciones verticales juntas parece proteger contra dependencias excesivas entre paquetes, ¿no?
Tim Visher
Casi nunca uso métodos protegidos y predeterminados. El 99% son públicos o privados. Algunas excepciones: (1) visibilidad predeterminada para métodos usados ​​solo por pruebas unitarias, (2) método abstracto protegido que se usa solo desde la clase base abstracta.
Esko Luontola
1
Tim Visher, las dependencias entre paquetes no son un problema, siempre que las dependencias apunten siempre en la misma dirección y no haya ciclos en el gráfico de dependencias.
Esko Luontola
0

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

quant_dev
fuente
¿Hay alguna razón para ir en cualquier dirección?
Tim Visher
Dividir por clase de activo (más generalmente: por dominio empresarial) facilita que las personas nuevas encuentren el camino a través del código. La división por función es buena para la encapsulación. Para mí, el acceso al "paquete" en Java es similar a un "amigo" en C ++.
quant_dev
@quant_dev No estoy de acuerdo con "..by function is good for encapsulation". Creo que quiere decir que es todo lo contrario. Además, no elija simplemente por "conveniencia". Hay un caso para cada uno (vea mi respuesta).
i3ensays
-3

Desde mi experiencia, la reutilización crea más problemas que la solución. Con los procesadores y la memoria más recientes y baratos, preferiría la duplicación de código en lugar de integrarlo estrechamente para reutilizarlo.

Raghu Kasturi
fuente
5
La reutilización de código no se trata del precio del hardware, sino de los costos de mantenimiento del código.
user2793390
1
De acuerdo, pero arreglar algo en un módulo simple no debería afectar el código completo. ¿De qué costes de mantenimiento estás hablando?
Raghu Kasturi
1
Una corrección de errores en un módulo debería afectar a toda la aplicación. ¿Por qué querría corregir el mismo error varias veces?
user2793390