软件可以看作是不同层的组合:
一层是实现层(大致就是功能层)
另一个是数据结构相互作用的方式(类级别,主要是 DIP 应该应用的地方)
另一种是组件应该交互的方式(包层)。如果可能的话,我们也想在这里应用某种 DIP。Robert C. Martin 坚持认为这一层主要依赖于业务(无论这意味着什么),因此原则略有不同:Stable-Dependencies Principle 和 Stable-Abstractions Principle(参见 Martin 的 Principle Pattern and Practice)
现在还应该强调软件工程中的原则,即只有在必须解决它们所解决的问题时才应该应用它们。只要您没有问题,就不要使用它们。
在类级别,如果您有充分的理由相信您的日志记录机制将由多个类实现,则应该使用 DIP。如果您认为暂时只有一种日志记录机制,那么不使用 DIP 完全没问题,因为没有要解决的问题。
现在应该在包级别做出同样的选择。但是,您的打包选择指南是部署。这里:
class ILogger {
virtual void log(const std::string& s) = 0;
};
class A : public ILogger {
…
};
class A2 : public ILogger {
…
};
- 如果您认为(出于商业原因)在没有 A2 的情况下发布 A 是有意义的,那么制作 4 个库:一个用于 ILogger,一个用于用户类 B,一个用于 A,一个用于 A2。
- 如果由于某些原因 A 和 A2 应该一起发布,那么只为 ILogger、A 和 A2 制作一个库。如果以后它们应该单独发布,那么打破你的图书馆,但不是现在,因为记住:YAGNI。
- 如果您对 ILogger 只有一个依赖项,那么只创建一个包含所有内容的库也是有意义的。
- 不要发布带有 ILogger 和 B 的库,以及带有 A 的另一个库,因为与解决方案 3 相比,您没有优势,这更复杂,并且可能进一步违反包的另一个原则:非循环依赖原则。
无论如何,这个决定主要取决于业务。还要记住,打包应该是自下而上的:只有当你有很多要组织的类时才创建一个新包。除非你没有这么多的课程,否则不要试图尽早做出决定,因为你几乎肯定会错。