2009-10-28 18 views
37

Trabajamos en un proyecto de tamaño medio (3 desarrolladores durante más de 6 meses) y necesitamos tomar la siguiente decisión: nos gustaría tener interfaces separadas de la implementación concreta. El primero es almacenar la interfaz en un archivo separado.¿Interfaces separadas de la implementación de clase en proyectos separados?

Nos gustaría ir más allá y separar los datos aún más: nos gustaría tener un proyecto (CSPROJ) con interfaz en un archivo .CS más otro archivo .CS con clases de ayuda (como algunas clases públicas utilizadas dentro de esta interfaz, algunas enumeraciones etc.). Entonces, nos gustaría tener otro proyecto (CSPROJ) con un patrón de fábrica, implementación de interfaz concreta y otras clases de "trabajadores".

Cualquier clase que desee crear un objeto que implemente esta interfaz debe incluir el primer proyecto que contiene las interfaces y las clases públicas, no la implementación en sí.

Esta solución tiene una gran desventaja: multiplica el número de ensambles por 2, ya que para cada proyecto "normal" uno tiene un proyecto con interacción y otro con implementación.

¿Qué recomendarías? ¿Cree que es una buena idea colocar todas las interfaces en un proyecto separado en lugar de una interfaz en su propio proyecto?

+0

Todas las interfaces son públicas y no considero Remoting, WCF y otras interfaces externas. –

Respuesta

52

yo distinguiría entre las interfaces de esta manera:

  1. las interfaces independientes cuyo propósito se puede describir sin hablar del resto de su proyecto. Colóquelos en un único "conjunto de interfaz" dedicado, que probablemente sea referenciado por todos los demás ensambles de su proyecto. Ejemplos típicos: ILogger, IFileSystem, IServiceLocator.

  2. Interfaces de clase acopladas que realmente solo tienen sentido en el contexto de las clases de su proyecto. Póngalos en el mismo conjunto que las clases a las que están acoplados.

    Un ejemplo: supongamos que su modelo de dominio tiene una clase Banana. Si recuperas plátanos a través de una interfaz IBananaRepository, entonces esa interfaz está estrechamente unida a los plátanos. Es imposible implementar o usar la interfaz sin saber algo sobre plátanos. Por lo tanto, es lógico que la interfaz resida en el mismo ensamblaje que Banana.

    El ejemplo anterior tiene un acoplamiento técnico, pero el acoplamiento podría ser lógico. Por ejemplo, una interfaz IFecesThrowingTarget solo tiene sentido como colaborador de la clase Monkey, incluso si la declaración de interfaz no tiene un enlace técnico al Monkey.

Mi respuesta no depende de la idea de que está bien tener alguna acoplamiento a clases.Esconder todo detrás de una interfaz sería un error. Sometimes it's okay to just "new up" a class, en lugar de inyectarlo o crearlo a través de una fábrica.

+29

+1 para arrojar heces en problemas de programación;) – Nate

+0

¡Opinión de interés! Probablemente lo haría como tú propones. –

+2

Definitivamente es una buena idea mover los tipos de infraestructura (como 'ILogger',' IFileSystem', etc.) en diferentes ensamblajes que los tipos específicos de dominio, pero aún debes poner 'ILogger' en el mismo ensamblado que tu concreto' Implementaciones de Logger a menos que haya una razón específica para no hacerlo (por ejemplo, porque una de las implementaciones concretas cambiará mucho, en cuyo caso podría tener sentido aislar esa clase en un ensamblaje separado). –

12

Sí, creo que esta es una buena idea. En realidad, lo hacemos aquí todo el tiempo, y finalmente tenemos que hacerlo por un simple motivo:

Usamos Remoting para acceder a la funcionalidad del servidor. Por lo tanto, los objetos remotos en el servidor deben implementar las interfaces y el código del cliente debe tener acceso a las interfaces para usar los objetos remotos.

En general, creo que estás más débilmente acoplado cuando pones las interfaces en un proyecto separado, así que solo ve y hazlo. No es realmente un problema tener 2 ensamblajes, ¿o sí?

ADEMÁS:

Sólo pasó por mi mente: Al poner las interfaces en un ensamblado independiente, que, además, consigue la ventaja de ser capaz de volver a utilizar las interfaces de si algunos de ellos son lo suficientemente generales.

+1

+1 por mencionar Remoting. (Editado para corregir errores tipográficos) – TrueWill

8

No lo haría a menos que ofrezca un beneficio comprobado para la arquitectura de su aplicación.

Es good to keep an eye on the number of assemblies you're creating. Incluso si una interfaz y su implementación se encuentran en el mismo conjunto, aún puede lograr el desacoplamiento que busca correctamente con un poco de disciplina.

+1

Tengo que estar de acuerdo contigo, Jeff. Resultó que una gran cantidad de asambleas implica muchos problemas. –

+5

Parece que este enlace está roto –

7

Creo que primero debe considerar si TODAS las interfaces pertenecen a la 'interfaz pública' de su proyecto.

Si van a ser compartidos por múltiples proyectos, ejecutables y/o servicios, creo que es justo ponerlos en un ensamblado por separado.

Sin embargo, si son solo para uso interno y para su comodidad, puede optar por mantenerlos en el mismo ensamblaje que la implementación, manteniendo así la cantidad total de ensamblajes relativamente baja.

+1

En realidad, lo que estaba hablando eran solo interfaces públicas. Los privados, si los hay, están contenidos en el proyecto con la implementación misma. –

+0

Ah, ya veo. En ese caso, quédese con la respuesta aceptada :-) –

2

Existen ventajas y desventajas en el enfoque, y también tendrá que atenuar la decisión con la mejor forma en su enfoque arquitectónico.

En el lado "pro", puede lograr un nivel de separación para ayudar a aplicar correctamente las interfaces.Considere que si tiene un desarrollador de nivel medio o medio que está trabajando en implementaciones, las interfaces mismas se pueden definir en un proyecto en el que solo tienen acceso de lectura. Quizás un jefe de equipo o arquitecto de alto nivel es responsable del diseño y mantenimiento de las interfaces. Si estas interfaces se usan en múltiples proyectos, esto puede ayudar a mitigar el riesgo de cambios involuntarios en otros proyectos cuando solo se trabaja en uno. Además, si trabajas con proveedores externos a los que distribuyes una API, empaquetar las interfaces es algo muy bueno.

Obviamente, hay algunas desventajas. El ensamblaje no contiene código ejecutable. En algunas tiendas en las que he trabajado, han desaprobado la funcionalidad en un ensamblado, independientemente de la razón. Definitivamente hay gastos generales adicionales. Dependiendo de cómo configure su archivo físico y la estructura del espacio de nombres, es posible que tenga varios ensambles haciendo lo mismo (aunque no es necesario).

En una nota semialeatoria, asegúrese de documentar bien sus interfaces. La herencia de la documentación de las interfaces usando GhostDoc es algo hermoso.

+0

No tenemos reglas tan estrictas como producir ensamblajes con código productivo solamente. Nuestro objetivo principal fue separar el ensamblaje de su implementación. Y sí, tratamos de documentarlos bien :) –

+1

Me alegra que no tengamos esa regla donde trabajo ahora también. ;-) –

3

Solíamos tener un buen número de ensamblajes separados en nuestro código compartido. Con el tiempo, descubrimos que casi invariablemente hacíamos referencia a estos en grupos. Esto hizo más trabajo para los desarrolladores, y tuvimos que buscar para encontrar en qué ensamblado estaba una clase o interfaz. Terminamos combinando algunos de estos ensambles en función de los patrones de uso. La vida se hizo más fácil.

Aquí hay muchas consideraciones: ¿está escribiendo una biblioteca para desarrolladores, está implementando las DLL a clientes externos, está utilizando la comunicación remota (gracias, Maximilian Mayerl) o escribiendo servicios WCF, etc. No hay nadie respuesta correcta - depende.

En general, estoy de acuerdo con Jeff Sternal: no rompa los conjuntos a menos que ofrezcan un beneficio comprobado.

5

Si una implementación de una interfaz termina teniendo muchas dependencias (en otros ensambles, etc.), tener la interfaz en un ensamble aislado puede simplemente ser una vida para los consumidores de mayor nivel.

Pueden hacer referencia a la interfaz sin depender inadvertidamente de las dependencias de la implementación específica.

Cuestiones relacionadas