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 ?
interface injection: the dependency provides an injector method that will inject the dependency into any client passed to it. Clients must implement an interface that exposes a setter method that accepts the dependency.
Interface Injection is similar to Getter and Setter DI, the Getter, and Setter DI use default getter and setter but Interface Injection uses support interface a kind of explicit getter and setter which sets the interface property.
To use DI, we must inject dependencies into other classes that depend upon them. These dependencies need an abstraction (which is usually an interface), and the dependent class will invoke that abstraction when it needs the corresponding functionality.
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With