我知道接口不能定义构造函数。强制所有实现接口的类在同一个合同中接收它们的依赖关系的最佳做法是什么。我知道可以通过属性将依赖项注入到对象中,但是通过构造函数传递它们对我来说更有意义。那怎么去DI?
答案 0 :(得分:7)
我知道你说你想要一份稳定的合同。但是不提供稳定接口的一个优点是,您的依赖关系可能随着不同的实现而变化很大,这会减少耦合:
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;
}
正如您所说,您还可以将依赖项实现为属性(或参数),但这会对您的依赖项进行硬编码,而不是使它们特定于实现。您将解耦特定的会话实现,但您仍然需要依赖会话。
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
// ...
}
构造函数注入仅在您使用某种依赖注入框架时才能工作,该框架可以为您构建/提供依赖项。即使没有框架,属性和参数注入也会起作用。
我相信这三种都是公认的做法。至少有几个流行的框架支持构造函数和属性注入。
这意味着由您决定对您的项目最有意义的决定取决于您。权衡是一种易于追踪的依赖图,与更强的耦合相比。该决定当然也不一定是所有构造函数或所有属性/参数。
要考虑的另一个更高级别的抽象是抽象工厂类。如果要对一组依赖项进行分组,或者需要在运行时构造它们的实例,则可以执行此操作:
public interface IInstallationFactory
{
IUser CreateRegisteredUser(Guid userSid);
IPackage CreateKnownPackage(Guid id);
IInstaller CreateInstaller();
}
各种框架也支持抽象工厂。
答案 1 :(得分:3)
一种选择是在接口上创建一个方法进行初始化。此方法可以接受所有必需的依赖项。
类似的东西:
void Configure(dependency1 value, etc.);
当然,使用框架进行此类初始化和DI有很多选择。有很多选择可供选择。
Scott Hanselman有一个很好的清单here。
答案 2 :(得分:2)
你需要做的是让你的所有接口实现子类化一个类,构造函数采用需要注入的任何状态。由于子类需要执行基本调用,因此在它们的构造函数中,您的约束会自动得到支持。
起初这可能看起来像一个奇怪的模式,但我们一直在企业解决方案中使用它,所以我保证它的理智: - )
答案 3 :(得分:0)
我们都知道这可以通过许多不同的方法实现,但有意义的东西肯定会更受欢迎。我定义了一些set-only
属性,然后该对象负责保存对传递给它的内容的引用:
public interface IBlogRepository
{
ISession Session { set; }
}
class BlogRepository : IBlogRepository
{
private ISession m_session;
ISession Session
{
set { m_session = value; }
}
}
这样,实现接口的每个类都知道set-only
属性是依赖注入,因为很少使用set-only
属性。我不确定这种方法是否被称为good practice
,但从现在开始对我来说就是这样。
答案 4 :(得分:0)
接口不负责依赖项。只有实施知道,履行合同需要什么。
可能有一个使用数据库的实现,另一个使用文件系统来持久保存数据。
接口声明需要哪个依赖项?数据库管理器还是文件系统管理器?