2008-11-18 9 views
6

Digamos que tenemosde inyección y por defecto sobrecargas del constructor

public interface ITimestampProvider 
{ 
    DateTime GetTimestamp(); 
} 

y una clase que lo consume

public class Timestamped 
{ 
    private ITimestampProvider _timestampProvider 

    public Timestamped(ITimestampProvider timestampProvider) 
    { 
     // arg null check 

     _timestampProvider = timestampProvider; 
    } 

    public DateTime Timestamp { get; private set; } 

    public void Stamp() 
    { 
     this.Timestamp = _timestampProvider.GetTimestamp(); 
    } 
} 

y una implementación por defecto de:

public sealed class SystemTimestampProvider : ITimestampProvider 
{ 
    public DateTime GetTimestamp() 
    { 
     return DateTime.Now; 
    } 
} 

¿Es útil o harfmful para presentar este constructor?

public Timestamped() : this(new SystemTimestampProvider()) 
{} 

Ésta es una pregunta general, es decir, sellado de tiempo no es la parte interesante.

+0

¿Qué sabor de la inyección de dependencia está utilizando CAB? ¿Castillo? –

+0

A los fines de esta pregunta, ninguno. Es una consulta API general. He actualizado la pregunta para eliminar la connotación de "inyección". –

Respuesta

7

Creo que depende del escenario, y es básicamente una función de quién es el consumidor el código (biblioteca frente a aplicación) y si está utilizando un contenedor IoC o no.

  • Si está utilizando un contenedor IoC, y esto no es parte de una API pública, a continuación, dejar que el recipiente haga el trabajo pesado, y sólo tienen el único constructor. Agregar el constructor no-args solo hace las cosas confusas, ya que nunca lo usarás.

  • Si esto es parte de una API pública, entonces mantenga ambas. Si está utilizando IoC, solo asegúrese de que su IoC encuentre el constructor "más codicioso" (el que tiene más argumentos). Las personas que no usan IoC, pero que usan su API apreciarán no tener que construir un gráfico de dependencia completo para usar su objeto.

  • Si no está utilizando un contenedor IoC, pero solo quiere probar la unidad con un simulacro, conserve el constructor no-args y convierta el codicioso constructor en interno. Agregue InternalsVisibleTo para su ensamble de prueba de unidad para que pueda usar el constructor codicioso. Si solo estás realizando pruebas unitarias, entonces no necesitas la superficie API pública adicional.

+0

Esto formará parte de una API pública, por lo que he decidido mantener la sobrecarga. Solo porque el valor predeterminado es razonable, incluso lo consideré. Quería que la pregunta estuviera en un vacío IoC para evitar comprometer el diseño por una preocupación puramente técnica. –

4

No proporcionaría ese constructor. Si lo hace, es demasiado fácil llamar a TimeStamped nuevo y obtener una instancia con SystemTimestampProvider() nuevo cuando su IoC se puede configurar para utilizar OtherTimestampProvider().

Al final del día terminarás tratando de depurar por qué estás recibiendo la marca de tiempo incorrecta.

Si solo proporciona el primer constructor, puede hacer un simple uso de SystemTimestampProvider para averiguar quién está (erróneamente) usando ese proveedor en lugar del proveedor configurado para IoC.

+0

Aunque IoC no es parte del escenario aquí, supongo que la mayoría de las implementaciones inyectarían el proveedor si está registrado, de lo contrario usaría el constructor predeterminado, lo que significa que solo obtendría 'SystemTimestampProvider' si no anula' ITimestampProvider', que es el exacto intención. ¿Pensamientos? –

3

En general, no lo creo ... Depende de para lo que esté usando Dependency Injection. Cuando uso DI para pruebas unitarias, hago lo mismo (más o menos) instanciando la versión de producción del objeto dependiente cuando la instancia inyectada es nula ... Y luego tengo una sobrecarga que no toma un parámetro y delega a la que sí ... Utilizo el parámetro para código de producción, e inyecto una versión de prueba para los métodos de prueba de la unidad ...

Si está hablando de una aplicación de contenedor IOC, otoh, necesita estar cuidado con lo que interfiere en los ajustes de configuración están diciendo el contenedor que hacer de una manera que no está claro ...

public class EventsLogic 
    { 
     private readonly IEventDAL ievtDal; 
     public IEventDAL IEventDAL { get { return ievtDal; } } 

     public EventsLogic(): this(null) {} 
     public EventsLogic(IIEEWSDAL wsDal, IEventDAL evtDal) 
     { 
      ievtDal = evtDal ?? new EventDAL(); 
     } 
    } 
0

trato de evitar esto - hay unos pocos lugares en los que he encontrado que es una diseño útil pero más a menudo t Si no lo he encontrado, solo me lleva a cometer errores que pueden resultar un poco desconcertantes.

La necesidad de los objetos inyectados predeterminados se reduce enormemente mediante el uso de un contenedor de inyección de dependencia (uso StructureMap) para gestionar todo este cableado: el contenedor DI asegura que siempre obtenga una instancia concreta que puede usar.

El único lugar donde todavía estoy tentado de usar el constructor que sugieres está en mis pruebas de unidad, pero recientemente he estado obteniendo un mayor valor de usar objetos falsos o burlados.

Hay lugares donde tener los objetos dependientes predeterminados es el diseño correcto y útil, pero en general diría que solo está introduciendo un acoplamiento firme que no agrega mucho valor.

0

No es útil ni perjudicial. Plantea un problema estético en el sentido de que usted está limitando su DI a la inyección del constructor solo cuando su diseño puede permitir la inyección del colocador de propiedades.

Otra opción sería la implementación de un captador que devuelve una implementación por defecto:

public DateTime Timestamp 
{ 
    get { return _timestampProvider??new SystemTimestampProvider(); } 
    set { _timestampProvider = value; } 
} 

Alternativamente, se puede implementar lo anterior utilizando un conjunto unitario si usted está preocupado acerca de cómo crear demasiados objetos en el montón.

0

Mi equipo tiene un gran éxito utilizando este método. Recomiendo un cambio:
Haga _TimestampProvider de solo lectura. Esto obliga al proveedor a ser determinista en la construcción y eliminará los errores.

public class Timestamped 
{ 
    private readonly ITimestampProvider _timestampProvider; 

    public Timestamped(ITimestampProvider timestampProvider) 
    { 
     _timestampProvider = timestampProvider; 
    } 

    public Timestamped(): this(new SystemTimestampProvider()) 
    { } 
} 

Dicho esto, estamos siempre buscando nuevas tecnologías, incluyendo los marcos de DI. Si alguna vez abandonamos esta técnica por algo significativamente mejor, se lo haremos saber.

Cuestiones relacionadas