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