He estado contemplando cómo equilibrar el diseño comprobable utilizando la inyección de dependencia con el suministro de API pública fija simple. Mi dilema es: la gente querría hacer algo así var server = new Server(){ ... }
y no tener que preocuparse por crear las muchas dependencias y gráficos de dependencias que Server(,,,,,,)
pueda tener. Durante el desarrollo, no me preocupo demasiado, ya que uso un marco IoC / DI para manejar todo eso (no estoy usando los aspectos de gestión del ciclo de vida de ningún contenedor, lo que complicaría aún más las cosas).
Ahora, es poco probable que las dependencias se vuelvan a implementar. La realización de componentes en este caso es casi exclusivamente para la capacidad de prueba (¡y un diseño decente!) En lugar de crear costuras para la extensión, etc. Las personas 99.999% de las veces desearán usar una configuración predeterminada. Entonces. Podría codificar las dependencias. ¡No quiero hacer eso, perdemos nuestras pruebas! Podría proporcionar un constructor predeterminado con dependencias codificadas y uno que tenga dependencias. Eso es ... desordenado, y probablemente sea confuso, pero viable. Podría hacer que la dependencia que recibe el constructor sea interna y hacer que mi unidad pruebe un ensamblaje amigo (suponiendo C #), que ordena la API pública pero deja una desagradable trampa oculta al acecho por mantenimiento. Tener dos constructores que están implícitamente conectados en lugar de explícitamente sería un mal diseño en general en mi libro.
Por el momento eso es lo menos malvado que se me ocurre. Opiniones? ¿Sabiduría?
HttpListener
constructor cambia, laServer
clase también debe cambiar. Están acoplados Una mejor solución es lo que @Winston Ewert dice a continuación, que es proporcionar una clase predeterminada con dependencias preespecificadas, fuera de la API de la biblioteca. Luego, el programador no está obligado a configurar todas las dependencias, ya que usted toma la decisión por él, pero aún tiene la flexibilidad para cambiarlas más adelante. Esto es un gran problema cuando tienes dependencias de terceros.En Java, en tales casos, es habitual tener una configuración "predeterminada" construida por una fábrica, que se mantiene independiente del servidor que se comporta "correctamente". Entonces, su usuario escribe:
mientras que el
Server
constructor todavía acepta todas sus dependencias y puede usarse para una personalización profunda.fuente
¿Qué hay de proporcionar una subclase que proporciona la "API pública"
El usuario puede
new StandardServer()
y estará en camino. También pueden usar la clase base del servidor si desean tener más control sobre cómo funciona el servidor. Este es más o menos el enfoque que uso. (No uso un marco ya que aún no he visto el punto).Esto todavía expone la API interna, pero creo que deberías. Ha dividido sus objetos en diferentes componentes útiles que deberían funcionar de forma independiente. No se sabe cuándo un tercero podría querer usar uno de los componentes de forma aislada.
fuente
Eso no me parece una "trampa oculta desagradable". El constructor público debería estar llamando al interno con las dependencias "predeterminadas". Mientras esté claro que el constructor público no debería modificarse, todo debería estar bien.
fuente
Estoy totalmente de acuerdo con tu opinión. No queremos contaminar el límite de uso de componentes solo con el propósito de realizar pruebas unitarias. Este es todo el problema de la solución de prueba basada en DI.
Sugeriría usar el patrón InjectableFactory / InjectableInstance . InjectableInstance son clases de utilidad reutilizables simples que son poseedores de valores mutables . La interfaz del componente contiene una referencia a este titular de valor que se inicializó con la implementación predeterminada. La interfaz proporcionará un método singleton get () que delegará al titular del valor. El cliente componente llamará al método get () en lugar de nuevo para obtener una instancia de implementación para evitar la dependencia directa. Durante el tiempo de prueba, opcionalmente podemos reemplazar la implementación predeterminada con una simulación. A continuación, usaré un ejemplo de TimeProviderclase, que es una abstracción de Date (), por lo que permite que las pruebas unitarias se inyecten y simulen para simular diferentes momentos. Lo siento, usaré Java aquí ya que estoy más familiarizado con Java. C # debería ser similar.
InjectableInstance es bastante fácil de implementar, tengo una implementación de referencia en Java. Para más detalles, consulte la publicación de mi blog Indirección de dependencia con fábrica inyectable
fuente