How to inject dependencies into classes that imple

2019-04-28 22:42发布

I know interfaces cannot define constructors. What is the best practice to force all classes implementing an interface, to receive their dependencies in a uniform contract. I know ints possible to inject dependencies into objects via properties, but passing them via constructors makes more sense to me. How to DI then ?

5条回答
趁早两清
2楼-- · 2019-04-28 22:54

One option is to create a method on the interface for initialization. This method can accept all of the required dependencies.

Something like:

void Configure(dependency1 value, etc.);

Of course, there are a lot of options to do this type of initialization and DI using a framework. There are a lot of options to choose from though.

Scott Hanselman has a good list here.

查看更多
Explosion°爆炸
3楼-- · 2019-04-28 23:03

The interface is not responsible for dependencies. Only the implementation knows, what it needs to fulfill the contract.

There could be one implementation using a database, another using file system to persist data.

Which Dependency should the interface declare required? The database manager or the filesystem manager?

查看更多
祖国的老花朵
4楼-- · 2019-04-28 23:11

I know you said you want to have a stable contract. But an advantage to not supplying a stable interface is that your dependencies could then vary wildly with different implementations, which would reduce coupling:

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;
}

As you've said, you can also implement dependencies as properties (or arguments), but this will hard-code your dependencies, rather than making them implementation specific. You will decouple your specific session implementations, but you still have to depend on sessions.

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
    // ...
}

Constructor injection will only work if you're using some sort of Dependency Injection framework that can handle constructing/supplying dependencies for you. Property and argument injection will work even without the framework.

I believe all three are accepted practice. At least a couple popular frameworks support both constructor and property injection.

This means the decision is up to you as to what makes the most sense for your project. The trade-off is a dependency graph that's easy to trace, vs stronger coupling. The decision certainly doesn't have to be all-constructor or all-property/argument, either.

Another higher-level abstraction to think about is an abstract factory class. You'd do this if you want to group a set of dependencies, or you need to construct instances of them at runtime:

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

Various frameworks also support abstract factories.

查看更多
混吃等死
5楼-- · 2019-04-28 23:13

We all know this is possible by many different methods, but something that makes sense is more welcome surely. I defined some set-only properties, then the object is responsible to holding a reference to what is passed to it:

public interface IBlogRepository
{
    ISession Session { set; }
}

class BlogRepository : IBlogRepository
{
   private ISession m_session;

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

This way every class implementing the interface knows that the set-only property is a dependency injection, since set-only properties are rarely used. I'm not sure if this method is known as a good practice or not, but for me it is, from now.

查看更多
甜甜的少女心
6楼-- · 2019-04-28 23:19

what you need to do is to have all your interface implementations subclass a class with a constructor taking whatever state that needs be injected. since the subclasses needs to perform a base-call, in their constructor, your constraints are uphold automatically.

at first this may seem like a strange pattern, but we use it all the time in our enterprise solutions, so I guarantee its sane :-)

查看更多
登录 后发表回答