2009-03-22 7 views
26

He estado ejecutando algunas métricas en mi proyecto Java y aparentemente hay muchos ciclos de dependencia entre paquetes. Realmente no sabía cómo organizar cosas en paquetes, así que hice lo que tenía sentido para mí, lo que aparentemente está mal.¿Cómo organizar paquetes (y evitar ciclos de dependencia)?

Mi proyecto es un marco de red neuronal. Las redes neuronales tienen neuronas, que están conectadas entre sí con conexiones. Necesitan depender el uno del otro. Sin embargo, también hay diferentes tipos de neuronas, por lo que pensé que sería una buena idea incluirlas en su propio paquete de "neuronas". Obviamente, una conexión no es una neurona, por lo que no debería estar en el paquete, pero dado que se refieren entre sí, ahora tengo una dependencia circular.

Esto es solo un ejemplo, pero tengo más situaciones como esta. ¿Cómo manejas este tipo de situaciones?

Además, he leído que las clases en un paquete más arriba en la jerarquía de paquetes no deben referirse a clases en paquetes que son más profundos. Esto significaría que una clase NeuralNetwork en el paquete 'nn' no puede referirse a la Neurona en el paquete 'nn.neuronas'. ¿Ustedes siguen este principio? ¿Y qué pasaría si moviera NeuralNetwork a 'nn.networks' o algo así? En ese caso, se referiría a un paquete de hermanos en lugar de a un niño. ¿Es esa una mejor práctica?

Respuesta

12

El antcontrib VerifyDesign task le ayudará a hacer lo que quiere:

Por ejemplo, si hay tres paquetes en el árbol una fuente

* biz.xsoftware.presentation 
* biz.xsoftware.business 
* biz.xsoftware.dataaccess 

y naturalmente presentación única debe depender de paquete de negocios y el negocio debe depender del acceso a la información. Si define su diseño de esta manera y se infringe la compilación fallará cuando se llame a la tarea de verificación de hormigas llamada . Por ejemplo, si crease una clase en biz.xsoftware.presentation y esa clase dependiera de una clase en biz.xsoftware.dataaccess, la compilación fallaría. Esto asegura que el diseño en realidad sigue lo que está documentado (a al menos un grado). Esto es especialmente agradable con automatizado construye

Así que una vez que haya decidido cómo las cosas deben ser organizados se puede hacer cumplir los requisitos en tiempo de compilación. También obtiene un control bien definido para que pueda permitir que ciertos casos rompan estas "reglas". Entonces puedes permitir algunos ciclos.

Dependiendo de cómo desee hacer las cosas, es posible que el paquete "utils" tenga sentido.

Para el caso particular de que usted cita ... yo podría hacer algo como esto:

  • paquete contiene nn Nueron y conexión
  • nn.neurons paquete contiene las subclases de Nueron

Neuron y Connection son conceptos de alto nivel utilizados en NeuralNetowrk, por lo que ponerlos todos juntos tiene sentido. Las clases Neuron y Connection pueden referirse entre sí, mientras que la clase Connection no tiene necesidad de conocer las subclases de Neuron.

+1

Recientemente desarrollé una herramienta similar que utiliza clases anotadas en lugar de un archivo de configuración central: https: // github.com/ruediste1/jabsaw – ruediste

5

No creo que las dependencias cíclicas como las que describes tengan como malas. Siempre que los conceptos que son interdependientes estén en el mismo nivel de abstracción y se relacionen con las mismas partes de la arquitectura, puede que no sea necesario ocultarlos entre sí. Las neuronas y las conexiones se ajustan a este proyecto de ley, según entiendo.

Una característica común para reducir dichos acoplamientos es extraer interfaces, e incluso colocarlas en un módulo aparte. Simplemente organizar por paquetes dentro de un solo proyecto no le permite ocultar los detalles de implementación lo suficiente. Un patrón común que le permite ocultar realmente puestas en práctica es como sigue:

código de cliente ----> Interfaces < --- Implementación

En este patrón, se oculta el módulo "Implementación" del código de cliente , lo que significa que el código en el módulo "Código de cliente" ni siquiera ve el código de implementación.

La anidación de paquetes tiene varias finalidades: algunos proyectos pueden tener un modelo de dominio organizado en paquetes. En este caso, los paquetes reflejan alguna agrupación del dominio, y las referencias pueden subir/bajar paquetes. Cuando se trata de cosas como la implementación de servicios, el patrón sugerido es bastante común y es bueno seguirlo. Mientras más profunda sea la jerarquía de paquetes, más específica se cree que es la clase.

+0

sí, verificar el artículo de diseño EXPLICITY repasa ese caso de uso aquí http://www.devx.com/opensource/Article/33729 –

4

¿De qué tipo de código estamos hablando? Si solo tienes de 10 a 20 clases, probablemente no necesites (y no deberías) organizar tu código en paquetes por el simple hecho de hacerlo.

A medida que su proyecto crece, la primera distinción que desea hacer es separar el código de la interfaz de usuario del modelo de datos subyacente y la lógica. Tener capas limpias separadas es crucial para poder hacer las pruebas unitarias correctas.

Si tiene problemas para deshacerse de las dependencias circulares, es probable que las clases sean realmente interdependientes y que residan en el mismo paquete.

Obtener las capas de abstracción correctas es probablemente uno de los aspectos más importantes a la hora de diseñar la estructura de código general.

5

¿Cómo manejas este tipo de situaciones?

Las dependencias circulares no son inherentemente malas. De hecho, esto a veces puede ser un caso de "la cura es peor que la enfermedad": extraer una interfaz aumenta el nivel de complejidad de su código y agrega otra capa de indirección. Probablemente no valga la pena para relaciones muy simples.

9

En primer lugar, le preocupa legítimamente porque las dependencias circulares entre paquetes son malas. Los problemas que surgen aumentan su importancia con el tamaño del proyecto, pero no hay razón para abordar esta situación a tiempo.

Debe organizar sus clases colocando las clases que reutiliza juntas en el mismo paquete. Entonces, si tiene, por ejemplo, AbstractNeuron y AbstractConnection, los ubicaría en el mismo paquete. Si ahora tiene las implementaciones HumanNeuron y HumanConnection, las colocaría en el mismo paquete (llamado, por ejemplo, * .network.human). O bien, puede tener solo un tipo de conexión, por ejemplo, BaseConnection y muchas Neuronas diferentes. El principio permanece igual. Coloca BaseConnection junto con BaseNeuron. HumanNeuron en su propio paquete junto con HumanSignal, etc. VirtualNeuron junto con VirtualSignal, etc. Usted dice: "Obviamente, una conexión no es una neurona, por lo que no debería estar en el paquete ...". Esto no es tan obvio ni correcto para ser exacto.

Dice que colocó todas sus neuronas en el mismo paquete. Ni esto es correcto, a menos que reutilices todas tus implementaciones juntas. De nuevo, eche un vistazo al esquema que describí arriba. O su proyecto es tan pequeño que coloca todo en un solo paquete, o comienza a organizar los paquetes como se describe. Para obtener más información, consulte The Common Reuse Principle:

LAS CLASES EN UN PAQUETE SE REUTILIZAN JUNTAS. SI USTED REUTILICE UNA DE LAS CLASES EN UN PAQUETE, USTED REUTILIZA ELLOS ALL.

Cuestiones relacionadas