2008-12-11 19 views
14

Estoy buscando inyección de dependencia, puedo ver los beneficios, pero estoy teniendo problemas con la sintaxis que crea. Tengo este ejemploalternativas de inyección de dependencia

public class BusinessProducts 
{ 
    IDataContext _dx; 

    BusinessProducts(IDataContext dx) 
    { 
     _dx = dx; 
    } 

    public List<Product> GetProducts() 
    { 
    return dx.GetProducts(); 
    } 
} 

El problema es que no quiero escribir

BusinessProducts bp = new BusinessProducts(dataContextImplementation); 

me gustaría seguir para escribir

BusinessProducts bp = new BusinessProducts(); 

porque siento la primera alternativa sólo se siente unatural . No quiero saber de qué depende BusinessProduct para obtener los productos, también creo que hace que mi código sea más ilegible.

¿Hay alguna alternativa a este enfoque, ya que me gustaría conservar mi sintaxis original para crear objetos pero me gustaría seguir falsificando las dependencias cuando las pruebas unitarias o esto puede hacer por mí?

estoy de codificación en C#, pero las alternativas de otros idiomas es bienvenida

+3

Bueno, si utiliza la palabra clave "nueva" directamente en lugar de solicitar el objeto, no está utilizando la inyección de dependencia. –

+4

@Pop, estás pensando en un marco DI, no en el patrón. En DI el patrón, inyectar una instancia de la clase de la que dependas en un constructor es perfectamente razonable. Otra forma es hacerlo a través de propiedades. – tvanfosson

+1

@Pop Catalin: este es un ejemplo de DI. La instancia del objeto que se inyectará es el "IDataContext", no los "BusinessProducts". – Owen

Respuesta

9

Uso una fábrica para mi contexto e inyecto, proporcionando un valor por defecto adecuado si la fábrica provista es nula. Lo hago por dos razones. Primero, utilizo el contexto de datos como un objeto con ámbito de unidad de trabajo, así que necesito poder crearlos cuando sea necesario, no mantener uno. En segundo lugar, principalmente uso DI para aumentar la capacidad de prueba, desacoplando solo una consideración secundaria.

Así que mi clase de productos comerciales se vería así:

public class BusinessProducts 
{ 
    private IDataContextFactory DataContextFactory { get; set; } // my interface 

    public BusinessProducts() : this(null) {} 

    public BusinessProducts(IDataContextFactory factory) 
    { 
      this.DataContext = factory ?? new BusinessProductsDataContextFactory(); 
    } 

    public void DoSomething() 
    { 
      using (DataContext dc = this.DataContextFactory().CreateDataContext()) 
      { 
      ... 
      } 
    } 

Una alternativa a esto sería para que la propiedad de la fábrica públicamente ajustable e inyectar una fábrica alternativo estableciendo la propiedad. De cualquier forma, si desea mantener el constructor nulo, deberá proporcionar un valor predeterminado.

3

Hay dos casos distintos: aquí

En el código de producción se quiere Nunca escritura

new BusinessProducts(dataContextImplementation) 

debido a la inyección de dependencias que normalmente será la creación de la jerarquía de objetos completo para usted. Esta es la naturaleza "viral" de los patrones de inyección de dependencia, ellos tienden a tomar el control total de la creación de su servicio.

En el código de prueba de unidad normalmente lo creará usted mismo, pero muy a menudo estará suministrando un objeto simulado o una implementación de inserción de datosContextImplementation. Por lo tanto, normalmente inyectará un objeto que no tiene una gran cantidad de dependencias posteriores.

+1

@krosenvold - Creo que estás pensando en marcos DI, no DI en el patrón. El patrón simplemente dice que usted hace exactamente eso: inyectar dependencias a través de constructores de propiedad o constructores. Puede usar el patrón DI sin usar un marco DI. – tvanfosson

6

Puede crear una fábrica. Los contenedores DI son mejores para cableados que ocurren en el momento de la configuración, no en tiempo de ejecución (como parece ser un caso). Las fábricas pueden implementarse de diferentes maneras, dependiendo de cuán conectable debe ser y cuántos lugares necesita para usarlo.

1

En general, el marco en sí tendrá la lógica para construir todo el árbol de objetos. Por ejemplo, en lugar de

new SomeObjectO(diContext) 

que llamarían el marco de esta manera:

DIFramework.GetNew<SomeObjectO>(); 

o

DIFramework.Get<SomeObject>(); 

Otro marco interesante para echar un vistazo a si le gustaría aprender acerca DI y el proceso son los proyectos Unity y Object Builder de Microsoft.

+0

thx, esta fue una respuesta aclaratoria – terjetyl

+1

¿Por qué es que casi todo el mundo está confundiendo el patrón con un marco DI? Los marcos DI usan el patrón DI, pero no es necesario usar un marco DI para implementar el patrón DI. – tvanfosson

+0

Estoy de acuerdo contigo tvanfosson. Esta respuesta me ayudó a entender qué hace un marco DI por ti – terjetyl

6

Normalmente tendría un constructor vacío que utiliza una instancia sólida (o instancias creadas por IoC), y una con DI. es decir,

public class BusinessProducts 
{ 
    IDataContext _dx; 

    BusinessProducts() 
    { 
     _dx = new SolidDataContext(); 
    } 

    BusinessProducts(IDataContext dx) 
    { 
     _dx = dx; 
    } 
} 

De esta forma puede usar DI para anular la instancia predeterminada en la prueba de prueba unitaria.

+0

He usado esta técnica varias veces para inyectar en diferentes implementaciones con el propósito de probar la unidad. Es útil si no tiene un contenedor DI. – RichardOD

1

Si realmente no te gusta inyectar esta instancia en el constructor, puedes intentar usar el CommonServiceLocator con tu framework .NET depedency injection framework compatible favorito. Esto permitirá escribir código como este:

public class BusinessProducts 
{ 
    IDataContext _dx; 

    BusinessProducts() 
    { 
     _dx = Microsoft.Practices.ServiceLocation.ServiceLocator.Current.GetInstance<IDataContext>(); 
    } 

    public List<Product> GetProducts() 
    { 
    return dx.GetProducts(); 
    } 
} 

Sin embargo, por favor ten en cuenta que esto no es lo que la mayoría de la gente espera cuando saben que utiliza un marco de inyección de dependencias. Creo que es mucho más común usar un marco de inyección de dependencia y dejar que cree todos los objetos para usted.

BusinessProducts bp = Microsoft.Practices.ServiceLocation.ServiceLocator.Current.GetInstance<BusinessProducts>(); 

Si desea evitar la ruta del marco de inyección de dependencias, usar una fábrica es probablemente la mejor manera de hacerlo.

1

Hay una técnica llamada del hombre pobre DI que tiene este aspecto

public class BusinessProducts 
{ 
    IDataContext _dx; 

    BusinessProducts() : this(new DataContext()) {} 

    BusinessProducts(IDataContext dx) 
    { 
     _dx = dx; 
    } 

    public List<Product> GetProducts() 
    { 
    return dx.GetProducts(); 
    } 
} 

Esto no es ideal, ya que le ata a la puesta en práctica pero es un buen paso hacia código desacoplado. esto es similar a @tvanfosson pero mucho más simple.

Secundo la recomendación de Windsor

1

Mi código hará referencia a Microsoft la unidad, pero estoy seguro de que es bastante aplicable a todos los marcos de DI. Si está usando DI correctamente, nunca necesita llamar a un nuevo BusinessObject (new dataContext), la asociación DI lo manejará todo por usted.

Mi ejemplo será un poco largo ya que pegaré en algún código que uso para ejecutar un sitio web de Model View Presenter completamente descargado por Unity. (Si desea que el código fuente completo echa un vistazo a mi blog y descargarlo desde mi servidor Assembla SVN)

carga del contenedor (puede estar en código como prefiero o el uso de la configuración)

protected void Application_Start(object sender, EventArgs e) 
{ 
    Application.GetContainer() 
     // presenters/controllers are per request     
     .RegisterType<IEmployeeController, EmployeeController>(new ContextLifetimeManager<IEmployeeController>()) 

     //Data Providers are Per session     
     .RegisterType<IEmployeeDataProvider, EmployeeDataProvider>(new SessionLifetimeManager<IEmployeeDataProvider>()) 

     //Session Factory is life time 
     .RegisterType<INHibernateSessionManager, NHibernateSessionManager>(new ContainerControlledLifetimeManager()); 
} 

HTTP personalizados llamadas de módulo Método Unity BuildUp para cada página durante la invocación OnPreRequest.

private static void OnPreRequestHandlerExecute(object sender, EventArgs e) 
{ 
    var handler = HttpContext.Current.Handler; 
    HttpContext.Current.Application.GetContainer().BuildUp(handler.GetType(), handler); 

    // User Controls are ready to be built up after the page initialization is complete 
    var page = HttpContext.Current.Handler as Page; 
    if (page != null) 
    { 
     page.InitComplete += OnPageInitComplete; 
    } 
} 

presentador contenedor Página decorado con [Dependencia] atributo

public partial class Employees : Page, IEmployeeView 
{ 
    private EmployeePresenter _presenter; 

    [Dependency] 
    public EmployeePresenter Presenter 
    { 
     set 
     { 
      _presenter = value; 
      _presenter.View = this; 
     } 
    } 
} 

Presentador con el método InjectionConstructor

public class EmployeePresenter : Presenter<IEmployeeView> 
{ 
    private readonly IEmployeeController _controller; 

    [InjectionConstructor] 
    } 
    public EmployeePresenter(IEmployeeController controller) 
    { 
     _controller = controller; 
} 

controlador sigue el juego

public class EmployeeController : IEmployeeController 
{ 
    private readonly IEmployeeDataProvider _provider; 

    [InjectionConstructor] 
    public EmployeeController(IEmployeeDataProvider DataProvider) 
    { 
     _provider = DataProvider; 
    } 
} 

mismo con Pro vider

public class EmployeeController : IEmployeeController 
{ 
    private readonly IEmployeeDataProvider _provider; 

    [InjectionConstructor] 
    public EmployeeController(IEmployeeDataProvider DataProvider) 
    { 
     _provider = DataProvider; 
    } 
} 

Por último, el administrador de sesión, que contiene solo un constructor común.

public class NHibernateSessionManager : INHibernateSessionManager 
{ 
    private readonly ISessionFactory _sessionFactory; 

    public NHibernateSessionManager() 
    {    
     _sessionFactory = GetSessionFactory(); 
    } 
} 

Entonces, ¿qué sucede cuando una solicitud de página se inicia la acumulación método() se llama en la página por el HttpModule. A continuación, Unity ve la propiedad marcada con el atributo Dependencia y verificará su contenedor para ver si existe dentro de él un objeto EmployeePresenter.

Como no hay tal objeto en el contenedor, intentará crear un EmployeePresenter. Después de la inspección para crear la clase que ve dentro del Presentador, se requiere un constructor que necesita un IEmployeeController inyectado en él. Como el contenedor en realidad tiene un administrador para el controlador, verá si existe una instancia en el contenedor que al inicio de la solicitud de página no existe, por lo que se utilizará para crear una instancia del controlador.

Unity verá que el controlador requiere un IEmployeeDataProvider inyectado en él, y continuará en este proceso hasta que finalmente llegue al punto donde el Proveedor necesita la inyección del administrador de sesión. Como el administrador de sesión ya no necesita inyectarse, Unity creará una instancia del gestor de sesión, lo almacenará en el contenedor para el contenedor ContainerLifeTimeManager, lo inyectará en el proveedor y lo almacenará, y así sucesivamente hasta que termine de crear un archivo. Dependencia de EmployeePresenter para la página.

4

Sus sentimientos, aunque son válidos, están fuera de lugar. El modelo Dependency Injection es una aplicación directa del principio Inversion of Control.

Esto significa que, en lugar de que su clase controle las instancias de otras clases que consume, esa relación es invertida y se le proporcionan las dependencias.

Como tal, sus clases exponen naturalmente sus dependencias mediante argumentos o propiedades de constructor. Mostrar desdén por esta estructura dice que realmente no has asimilado el patrón.

Cuestiones relacionadas