2010-11-06 14 views
6

Sé que las interfaces no pueden definir constructores. ¿Cuál es la mejor práctica para obligar a todas las clases que implementan una interfaz a recibir sus dependencias en un contrato uniforme? Sé que es posible inyectar dependencias en objetos a través de propiedades, pero pasarlas a través de constructores tiene más sentido para mí. ¿Cómo DI entonces?Cómo inyectar dependencias en clases que implementan una interfaz?

Respuesta

0

Todos sabemos que esto es posible por muchos métodos diferentes, pero algo que tiene sentido es más bienvenido.He definido algunas set-only propiedades, entonces el objeto es responsable de la celebración de una referencia a lo que se le ha pasado:

public interface IBlogRepository 
{ 
    ISession Session { set; } 
} 

class BlogRepository : IBlogRepository 
{ 
    private ISession m_session; 

    ISession Session 
    { 
     set { m_session = value; } 
    } 
} 

De esta manera cada clase que implementa la interfaz sabe que la propiedad set-only es una inyección de dependencia, ya que set-only propiedades son raramente usado. No estoy seguro si este método se conoce como good practice o no, pero para mí lo es, a partir de ahora.

+0

Apegarse estrictamente a una sola forma de definir dependencias es descartar opciones de diseño. Las otras opciones no son malas, solo son diferentes. –

+0

Con este diseño, necesita clases derivadas para definir las propiedades, pero no requiere que los usuarios de la clase completen dependencias. Si un usuario instancia su clase, no completa esas propiedades y llama a un método, no puede lanzar. Hacerlo violaría el contrato implícito definido por la interfaz. Las únicas dos formas de * requerir * que se complete una dependencia son los parámetros constructor y de método. –

3

Una opción es crear un método en la interfaz para la inicialización. Este método puede aceptar todas las dependencias requeridas.

Algo así como:

void Configure(dependency1 value, etc.); 

Por supuesto, hay una gran cantidad de opciones para hacer este tipo de inicialización y DI utilizando un marco. Sin embargo, hay muchas opciones para elegir.

Scott Hanselman tiene una buena lista here.

2

lo que necesita hacer es tener todas sus subclases de implementaciones de interfaz una clase con un constructor tomando cualquier estado que necesite ser inyectado. dado que las subclases necesitan realizar una llamada base, en su constructor, sus restricciones se mantienen automáticamente.

Al principio esto puede parecer como un patrón extraño, pero lo usamos todo el tiempo en nuestras soluciones empresariales, por lo que garantizar su sano juicio :-)

7

Sé que dijo que desea tener un contrato estable. Sin embargo, una ventaja para no suministrar una interfaz estable es que sus dependencias a continuación podrían variar mucho con diferentes implementaciones, lo que reduciría acoplamiento:

public interface IBlogRepository 
{ 
    IEnumerable<Entry> GetEntries(int pageId, int pageCount); 
} 

class BlogDatabase : IBlogRepository 
{ 
    public BlogDatabase(ISession session) 
    { 
     this.session = session; 
    } 

    public IEnumerable<Entry> GetEntries(int pageId, int pageCount) 
    { 
     // Not that you should implement your queries this way... 
     var query = session.CreateQuery("from BlogEntry"); 
     return query.Skip(pageId * pageCount).Take(pageCount); 
    } 

    private ISession session; 
} 

Como usted ha dicho, también se puede aplicar dependencias como las propiedades (o argumentos), pero esto codificará sus dependencias en lugar de hacer que sean específicas de la implementación. Desvinculará las implementaciones de sesión específicas, pero aún debe depender de las sesiones.

public interface IBlogRepository 
{ 
    ISession Session { get; set; } 
    IEnumerable<Entry> GetEntries(int pageId, int pageCount); 
    IEnumerable<Entry> GetEntriesWithSession(ISession session, 
     int pageId, int pageCount); 
} 

class BlogDatabase : IBlogRepository 
{ 
    public ISession Session { Get; set; } 

    public IEnumerable<Entry> GetEntries(int pageId, int pageCount) 
    { 
     var query = Session.CreateQuery ... 
    } 

    public IEnumerable<Entry> GetEntries(ISession session, int pageId, int pageCount) 
    { 
     var query = session.CreateQuery ... 
    } 
} 

class BlogFile : IBlogRepository 
{ 
    // ISession has to abstract a file handle. We're still okay 
    // ... 
} 

class BlogInMemory : IBlogRepository 
{ 
    // ISession abstracts nothing. 
    // Maybe a lock, at best, but the abstraction is still breaking down 
    // ... 
} 

inyección de constructor sólo funcionará si está utilizando algún tipo de marco de inyección de dependencia que puede manejar la construcción/el suministro de dependencias para usted. La inyección de propiedades y argumentos funcionará incluso sin el marco.

Creo que las tres son prácticas aceptadas. Al menos un par de frameworks populares son compatibles tanto con el constructor como con la inyección de propiedades.

Esto significa que la decisión depende de usted en cuanto a qué tiene más sentido para su proyecto. El trade-off es un gráfico de dependencia que es fácil de rastrear, frente a un acoplamiento más fuerte. La decisión ciertamente no tiene que ser todo-constructor o toda-propiedad/argumento, tampoco.

Otra abstracción de alto nivel para pensar es una clase abstracta de fábrica. Se podría hacer esto si desea agrupar un conjunto de dependencias, o que necesita para construir instancias de las mismas en tiempo de ejecución:

public interface IInstallationFactory 
{ 
    IUser CreateRegisteredUser(Guid userSid); 
    IPackage CreateKnownPackage(Guid id); 
    IInstaller CreateInstaller(); 
} 

Varios marcos también apoyan fábricas abstractas.

+1

Esto es exactamente lo que estaba diciendo, pero mucho mejor explicado. +1 y eliminando el mío. –

0

La interfaz no es responsable de las dependencias. Solo la implementación sabe lo que necesita para cumplir el contrato.

Podría haber una implementación usando una base de datos, otra usando un sistema de archivos para persistir datos.

¿Qué dependencia debe declarar la interfaz requerida? ¿El administrador de la base de datos o el administrador del sistema de archivos?

Cuestiones relacionadas