Reputation: 30857
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 ?
Upvotes: 8
Views: 5135
Reputation: 91
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?
Upvotes: 0
Reputation: 30857
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.
Upvotes: 0
Reputation: 59111
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.
Upvotes: 7
Reputation: 13832
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 :-)
Upvotes: 2
Reputation: 5439
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.
Upvotes: 3