我知道你说过你想要一份稳定的合同。但有一个优势not提供稳定的接口的问题是,您的依赖关系可能会随着不同的实现而发生很大的变化,这将减少耦合:
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();
}
各种框架也支持抽象工厂。