¿Deben colocarse las interfaces en un paquete separado? [cerrado]

80

Soy nuevo en un equipo que trabaja en un proyecto bastante grande, con muchos componentes y dependencias. Para cada componente, hay un interfacespaquete donde se colocan las interfaces expuestas para ese componente. ¿Es esta una buena practica?

Mi práctica habitual siempre ha sido que las interfaces y las implementaciones vayan en el mismo paquete.

kctang
fuente
4
¿Por qué está etiquetado como [idioma independiente]?
finlandés
Otra situación en la que esto podría ser preferible si el módulo se va a comunicar a través de algún rpc en lugar de una invocación directa: ¿entonces la interfaz es útil para generar proxies para el cliente?
redzedi

Respuestas:

101

Colocar tanto la interfaz como la implementación es un lugar común y no parece ser un problema.

Tomemos, por ejemplo, la API de Java: la mayoría de las clases tienen ambas interfaces y sus implementaciones incluidas en el mismo paquete.

Tomemos, por ejemplo, el java.utilpaquete:

Contiene los interfaces como Set, Map, List, mientras que también tiene las implementaciones tales como HashSet, HashMapy ArrayList.

Además, los Javadocs están diseñados para funcionar bien en esas condiciones, ya que separa la documentación en las vistas Interfaces y Clases al mostrar el contenido del paquete.

Tener paquetes solo para interfaces puede ser un poco excesivo, a menos que haya una gran cantidad de interfaces. Pero separar las interfaces en sus propios paquetes por el simple hecho de hacerlo parece una mala práctica.

Si es necesario diferenciar el nombre de una interfaz de una implementación, se podría tener una convención de nomenclatura para facilitar la identificación de las interfaces:

  • Prefije el nombre de la interfaz con un I. Este enfoque se toma con las interfaces en el marco .NET. Sería bastante fácil decir que IListes una interfaz para una lista.

  • Utilice el ablesufijo - . Este enfoque se ve a menudo en la API de Java, como por ejemplo Comparable, Iterabley Serializablepara nombrar unos pocos.

coobird
fuente
43
+1 aunque no recomendaría la convención de nomenclatura I * a menos que esté en .NETverse, e incluso entonces solo por coherencia
CurtainDog
7
He usado esta convención en código java y la he encontrado útil ... YMMV
hhafez
5
Sé que esto es un poco viejo, pero lo que he encontrado que se sugiere ahora es nombrar la interfaz claramente y nombrar la implementación algo más extraño. Por ejemplo, Interfaz = Implementación de tienda = StoreImpl Principalmente para resaltar que la interfaz debe usarse sobre la implementación.
helado maravilloso
1
Creo que este enfoque, aunque parezca correcto, trae consigo problemas arquitectónicos. Por ejemplo, no hay forma de definir bibliotecas que usen solo las interfaces y luego implementarlas en una biblioteca común, que es la capa de infraestructura.
Luca Masera
1
Estoy de acuerdo con el comentario sobre no usar el prefijo "I" para las interfaces de la misma manera que no recomendaría usar el sufijo "impl" para las implementaciones, este es un buen artículo que reflexiona sobre esos casos: octoperf.com/blog/2016 / 10/27 / impl-classes-are-evil
raspacorp
19

Para cualquier idioma, juntarlos en el mismo paquete está bien. Lo importante es lo que está expuesto al mundo exterior y cómo se ve desde fuera. A nadie sabrá ni le importará si la implementación está en el mismo paquete o no.

Veamos este caso en particular.

Si tiene todos los elementos públicos en un paquete y los elementos privados en otro paquete que no está expuesto públicamente, el cliente de la biblioteca ve un paquete. Si mueve las cosas privadas al paquete con las cosas expuestas públicamente, pero no las expone dentro del paquete, el cliente ve exactamente lo mismo.

Por lo tanto, esto tiene el olor de una regla sin una buena razón: es tomar una decisión basada en algo que es públicamente visible sin que esa decisión tenga ningún efecto en lo que es públicamente visible.

Dicho esto, si en algún caso particular le parece una buena idea dividir la interfaz y la implementación en paquetes separados, siga adelante y hágalo. Las razones para hacer esto que me vienen a la mente son que el paquete es enorme o que tiene una implementación alternativa a la que podría querer vincular en lugar de la estándar.

cjs
fuente
Dada la etiqueta 'Java' en la pregunta: ¿cuál es el significado de un ' paquete expuesto públicamente ' en Java? ¿O su respuesta no es específica de Java? Pensé que solo los miembros del paquete (como clases, interfaces y sus miembros), no los paquetes en sí, tenían especificadores de control de acceso como 'público', 'privado', etc. (Esto se discute más en @ question stackoverflow.com/questions/4388715/… )
bacar
Acabo de aclarar la redacción; por "paquete expuesto públicamente" realmente quise decir "el paquete con las cosas expuestas públicamente".
cjs
Seguramente todos los paquetes deberían tener cosas públicas; si no es así, ¿cómo se puede utilizar el paquete?
bacar
En este caso particular, hubo paquetes sin nada público, pero que fueron utilizados por paquetes que hicieron las cosas públicas. Por lo tanto, el primero no puede usarse directamente, sino solo indirectamente a través del segundo.
cjs
1
Pero se aplica la misma pregunta. ¿Cómo pueden los paquetes que hicieron las cosas públicas usar los paquetes que no hicieron nada público? No conozco ninguna forma en Java de hacer cumplir "lo primero no se puede usar directamente, sino sólo indirectamente a través de lo segundo". Si su paquete de implementación solo tiene miembros privados, es tan inaccesible para su paquete de interfaz como para el cliente. & de manera similar para otros ámbitos de acceso. No hay equivalente de friend(C ++) o internal(C #) en Java.
bacar
10

En muchos marcos, como OSGi, casi es necesario. Creo que esto promueve un acoplamiento más flojo, en el paquete en lugar del nivel del tarro.

javamonkey79
fuente
7

Un argumento para poner interfaces en diferentes paquetes es que es más fácil crear frascos 'api' que se pueden distribuir a los consumidores de su producto o servicio. Es perfectamente posible hacer esto con interfaces e implementaciones juntas, pero es más sencillo crear un script si están en paquetes diferentes.

Cameron Pope
fuente
7
Puede tener el proyecto de API separado (siendo un distribuible separado) y eso aún no impide que las interfaces y la implementación estén en el mismo paquete. Un poco como si tuvieras pruebas unitarias en el mismo paquete que las clases probadas pero aún en un artefacto de compilación separado. Los jar y los paquetes son bastante ortogonales en Java.
George
7

El libro Practical Software Engineering: A Case-Study Approach aboga por poner interfaces en proyectos / paquetes separados.

La arquitectura PCMEF + de la que habla el libro tiene los siguientes principios:

  1. Principio de dependencia descendente (DDP)
  2. Principio de notificación ascendente (UNP)
  3. Principio de comunicación vecina (NCP)
  4. Principio de asociación explícita (EAP)
  5. Principio de eliminación de ciclo (CEP)
  6. Principio de denominación de clases (CNP)
  7. Principio del paquete de conocidos (APP)

La descripción de los principios n. ° 3 y n. ° 7 explica por qué es una buena idea:

El principio de comunicación del vecino exige que un paquete solo pueda comunicarse directamente con su paquete vecino. Este principio asegura que el sistema no se desintegre en una red incompresible de objetos intercomunicados. Para hacer cumplir este principio, el paso de mensajes entre objetos no vecinos utiliza la delegación (Sección 9.1.5.1). En escenarios más complejos, el paquete de conocimiento (Sección 9.1.8.2) se puede utilizar para agrupar interfaces para ayudar en la colaboración que involucra paquetes distantes.

El principio del paquete de conocidos es la consecuencia del principio de comunicación con el vecino. El paquete conocido consta de interfaces que pasa un objeto, en lugar de objetos concretos, en argumentos para llamadas a métodos. Las interfaces se pueden implementar en cualquier paquete PCMEF. Esto permite de manera efectiva la comunicación entre paquetes no vecinos y, al mismo tiempo, centraliza la administración de dependencias en un solo paquete conocido. La necesidad de un paquete de conocimiento se explicó en la Sección 9.1.8.2 y se analiza nuevamente a continuación en el contexto del PCMEF.

Vea este enlace: http://comp.mq.edu.au/books/pse/about_book/Ch9.pdf

Kenci
fuente
5

Sí, es una muy buena práctica, porque te permite publicar la interfaz sin publicar tu implementación específica. Dicho esto, si no tiene necesidad de publicar interfaces externas, no hay problema en poner las definiciones de interfaz en el mismo paquete que la implementación.

Paul Sonier
fuente
2
Quizás si está definiendo interfaces para el JCP o algo, esto tiene sentido, pero si está produciendo múltiples implementaciones concretas de estas interfaces para su proyecto, entonces parece una carga innecesaria empaquetar las interfaces por separado.
Brian Reiter
2
No es una gran carga, en cualquier caso ...
Paul Sonier
4
-1 Sí, lo es. Hace que sea innecesariamente difícil encontrar las implementaciones y separa las cosas que van juntas.
Michael Borgwardt
5
Y no publicar las implementaciones es un asunto completamente diferente que se puede hacer haciéndolos privados del paquete, y NO se puede hacer colocándolos en un paquete separado. No existe un paquete no público.
Michael Borgwardt
1
Además, parece que todas las interfaces son innecesariamente públicas.
Adeel Ansari
4

Hacemos esto donde yo trabajo (es decir: poner la interfaz en un paquete y la implementación en otro) y la principal ventaja que obtenemos de esto es que podemos intercambiar fácilmente entre implementaciones.

hhafez
fuente
7
¿Cómo le ayuda a cambiar las implementaciones?
Janusz
vea la respuesta de Cameron Pope, es exactamente la razón por la que lo estamos haciendo aquí
hhafez
3

No tengo mucha experiencia en Java, pero me gusta hacer esto como una buena práctica en C # /. NET porque permite una expansión futura donde los ensamblados con las clases concretas que implementan las interfaces pueden no distribuirse hasta el cliente porque son proxy de una fábrica de intermediarios o por cable en un escenario de servicio web.

Jesse C. Rebanadora
fuente
2

Normalmente pongo interfaces con la implementación, sin embargo, puedo ver por qué es posible que desee mantenerlas separadas. Digamos, por ejemplo, que alguien quisiera volver a implementar clases en función de sus interfaces, necesitaría un jar / lib / etc con su implementación en lugar de solo las interfaces. Con ellos separados, podría simplemente decir "Aquí está mi implementación de esa interfaz" y terminar con ella. Como dije, no es lo que hago, pero puedo ver por qué algunos querrían hacerlo.

Matt_JD
fuente
2

Estoy haciendo esto en un proyecto y me está funcionando bien por estas razones:

  • Las interfaces e implementaciones empaquetadas por separado son para un caso de uso diferente al de los diversos tipos de mapas o conjuntos. No hay ninguna razón para tener un paquete solo para árboles (java.util.tree.Map, java.util.tree.Set). Es solo una estructura de datos estándar, así que júntela con las otras estructuras de datos. Sin embargo, si se trata de un juego de rompecabezas que tiene una interfaz de depuración realmente simple y una interfaz de producción bonita, como parte de su interfaz, es posible que tenga un com.your.app.skin.debug y un com.your.app .piel.muy. No los pondría en el mismo paquete, porque hacen cosas diferentes y sé que recurriría a SmurfNamingConvention (DebugAttackSurface, DebugDefenceSurface, PrettyAttackSurface, etc.) para crear un espacio de nombres informal para los dos si estuvieran en el mismo paquete.

  • Mi solución para el problema de encontrar interfaces e implementaciones relacionadas que se encuentran en paquetes separados es adoptar una configuración de nombres para mis paquetes. Por ejemplo, puedo tener todas mis interfaces en com.your.app.skin.framework y saber que otros paquetes en el mismo nivel del árbol de paquetes son implementaciones. La desventaja es que se trata de una convención poco convencional. Honestamente, veré lo buena que es esta convención en 6 meses :)

  • No uso esta técnica religiosamente. Hay interfaces que solo tienen sentido en una implementación particular. No los pego en el paquete del marco. Hay algunos paquetes en los que no parece que vaya a crear 40 clases de implementación diferentes, así que no me molesto.

  • Mi aplicación usa guice y tiene muchas interfaces.

Las preguntas sobre el diseño de programas generalmente involucran beneficios e inconvenientes y no existe una respuesta única para todos. ¿Por qué alguna vez usarías esta técnica en un pequeño programa de 200 líneas? Tiene sentido para mí, dadas mis otras opciones arquitectónicas, para mi problema específico. :)

Ictericia de Moray
fuente
1

Los prefiero en el mismo paquete. No tiene sentido crear un paquete específicamente para interfaces. Esto es especialmente redundante para los equipos que simplemente aman los prefijos y sufijos de su interfaz (por ejemplo, "I" e "Impl" para Java).

Si es necesario publicar un conjunto de interfaces como API pública, tiene más sentido mantenerlas en un proyecto completamente separado y crear dependencias del proyecto en su lugar. Pero supongo que todo se reduce a la preferencia y conveniencia de la situación.

aberrante80
fuente
0

Colóquelos en paquetes que reflejen sus proyectos. Está bien y es común juntar interfaces e implementaciones si son parte del mismo proyecto, pero si está escribiendo una API, es probable que alguien más elija un nombre de paquete relevante para su proyecto.

En general, si es para el mismo proyecto, no veo ningún beneficio en mantener las interfaces en un paquete separado de sus impls. Si está abarrotado, puede haber otros problemas de nombres de paquetes, independientemente de la disposición de la interfaz.

Rog
fuente
0

Creo que es importante tener en cuenta que para el marco OSGi, es mejor que estén en diferentes paquetes para que pueda exportar fácilmente un paquete completo mientras oculta los paquetes de implementación.

jkabugu
fuente
-1

Hay muchas buenas razones para colocar las interfaces en un paquete separado de la implementación. Por ejemplo, es posible que desee utilizar un contenedor que admita Dependency Injection para conectar las implementaciones necesarias en el tiempo de ejecución, en cuyo caso solo la interfaz debe estar presente en el momento de la compilación y la implementación se puede proporcionar en el tiempo de ejecución. Alternativamente, es posible que desee proporcionar múltiples implementaciones (por ejemplo, usando implementaciones simuladas para pruebas) o múltiples versiones de una implementación particular en tiempo de ejecución (por ejemplo, para pruebas A / B, etc.). Para este tipo de casos de uso, es más conveniente empaquetar la interfaz y la implementación por separado.

Tyson
fuente
No es necesario que coloque la implementación en un paquete diferente para hacer ninguna de las cosas que enumeró
Crowie
Eso es justo, pero sigo pensando que la diferencia en los requisitos de "estar presente en el tiempo de compilación frente al tiempo de ejecución" es un caso convincente para empaquetar por separado. Actualicé el texto para tratar de reflejar eso con mayor claridad.
Tyson