6

Me gusta la inyección de Constructor para la inyección de dependencia. Obliga a una declaración clara de inquietudes de un tipo y ayuda con la capacidad de prueba.Alternativas de inyección de constructor (Castle Windsor)

me gusta la inyección de constructor, en la mayoría lugares ...

registro como un ejemplo en el que no gusta. Si tengo una clase base de la que heredan muchas otras clases, y quiero que todas esas clases utilicen una instancia de mi ILogger (o lo que sea), y no quiero una fábrica estática (Logger.Instance) ... no quiero tener que declarar un constructor en cada subclase que tome un ILogger.

Así, pude tener mi clase base declarar el registrador como una propiedad y tienen que inyectarse esa manera

public class MyBaseClass 
{ 
    public ILogger Logger { get; set; } 
} 

... pero

  1. que no me asegurar que Logger en realidad, se inyecta y no es nulo.
  2. no me gusta tener ILogger con un conjunto público

Así que ... ¿qué otras opciones tengo? (Estoy usando Castle Windsor).

he contemplado hacer una interfaz de

public interface IInitializable<T> 
{ 
    void Initialize(T instance); 
} 

public class MyBaseClass : IInitializable<ILogger>, ...could have other IInitializables too... 
{ 
    protected ILogger Logger { get; private set; } 

    public void Initialize(ILogger instance) 
    { 
     Logger = instance; 
    } 
} 

luego tener una instalación en mi contenedor que llama automáticamente a todas las implementaciones de IInitializable<T> sobre construcción de tipo ...

Pero me pregunto qué otros pueblos 'pensamientos son antes de ir por esa ruta ...

+0

¿Por qué no desea utilizar un patrón estático de fábrica? –

+0

Ctor injection, etc., es muy útil para variadas implementaciones para pruebas, etc., de forma individual, pero ¿realmente lo necesita para iniciar sesión? ¿Realmente necesita la capacidad de variar la implementación de registro en el nivel de instancia? El patrón de fábrica estático todavía le dará la capacidad de variar la implementación del registro * en general * que generalmente es lo que se requiere. –

+0

La implementación de lo que Logger devolver debe ser dinámico. Específicamente Logger.Instance debería ser diferente según el contexto (es decir, operación WCF, cliente WPF, cliente SL, etc.). Supongo que podría hacer un localizador de servicios dentro. Ahora, pero eso es como un antipatrón como lo entiendo ... – Jeff

Respuesta

3

Estás complicar esto. El recommended and documented pattern to inject ILogger es tener NullLogger.Instance por defecto (es decir, un null object pattern) y hacer que el Logger una dependencia opcional. No hay nada de malo en tener un organismo público para el registrador. Usar un IInitializable personalizado como el que muestra probablemente solo complicará las cosas y no aportará ningún valor real.

voy a copiar la muestra a partir de la documentación aquí para una fácil referencia:

using Castle.Core.Logging; 

public class CustomerService 
{ 
    private ILogger logger = NullLogger.Instance; 

    public CustomerService() 
    { 
    } 

    public ILogger Logger 
    { 
     get { return logger; } 
     set { logger = value; } 
    } 

    // ... 
} 

EDIT: Parece que la cuestión es en realidad acerca de tener diferentes implementaciones del registrador dependiendo del contexto (que tiene poco que ver con la pregunta original). Si ese es el caso, use reemplazos de servicios o selectores de controladores.

+0

¿Alguna sugerencia sobre cómo evitar modificaciones? ¿Qué sucede si quiero que las clases heredadas puedan aprovechar un servicio pero no cambien su implementación? – Jeff

+1

@ JeffN825: ¿por qué alguien lo modificaría en primer lugar? –

+0

@ JeffN825: Por cierto, no puedes tenerlo de ambas formas. Es una inyección de '' solo lectura privada '' y constructora, o una propiedad mutable. No hay otra opción en .net que yo sepa. –

0

Si su propia clase base no es en función de la ILogger, debería quitar la propiedad de la clase base. De esta forma, mantendrá limpio el constructor de la clase base.

De lo contrario puede crear una fábrica que es capaz de crear descendientes de MyBaseClass.Esto podría tener este aspecto:

public interface IMyBaseClassFactory 
{ 
    MyBaseClass CreateNew<TMyBaseClass>() where TMyBaseClass : MyBaseClass; 
} 

Ahora puede crear un registro de una implementación de IMyBaseClassFactory que es capaz de crear nuevas instancias y registrar las dependencias opcionales:

public MyBaseClassFactoryImpl : IMyBaseClassFactory 
{ 
    public MyBaseClass CreateNew<TMyBaseClass>() 
    { 
     // Call your IoC container to get a descendant. 
     var instance = ServiceLocator.GetInstance<TMyBaseClass>(); 

     // Register missing dependencies 
     instance.Logger = ServiceLocator.GetInstance<ILogger>(); 

     return instance; 
    } 
} 

contenedores más IoC te permiten decorar inyectable propiedades con atributos. Sin embargo, la ventaja de utilizar una fábrica es que su aplicación ignorará completamente el marco de trabajo de IoC utilizado. Como inconveniente, hay más trabajo por hacer (crear una fábrica, inyectar esa fábrica en lugar de la instancia misma y llamar a la fábrica para crear una nueva instancia).

Al definir una propiedad inyectable, debe hacer que sea de lectura/escritura. Por supuesto, no querrás que nadie restablezca accidentalmente esa propiedad después. Sin la inyección del constructor, es difícil lograr esto con el soporte de tiempo de compilación. Sin embargo, se puede realizar fácilmente un control del tiempo de ejecución:

private ILogger logger; 

public ILogger Logger 
{ 
    get { return this.logger; } 
    set 
    { 
     if (this.logger != null) 
     { 
      throw new InvalidOperationException("Logger has already been set."); 
     } 

     this.logger = value; 
    } 
} 

Espero que esto ayude.

Cuestiones relacionadas