依赖倒置原则对项目结构有哪些影响?

Ste*_*ser 8 c++ deployment dependencies modularity dependency-inversion

如果我想使用DIP开发一个假设的模块化C++项目.由于模块化,我选择在一个库中完全实现一个特定功能A.另一个库B(或两个或三个......)正在使用此功能(例如,日志记录机制):

class ILogger
{
    virtual void log(const std::string& s) = 0;
};
Run Code Online (Sandbox Code Playgroud)

我应该把这个界面放在哪里?一些博主似乎建议,因为界面属于其用户(因为DIP),你应该将界面放在用户端(或这里).这也可以提高可测试性,因为您不需要任何实现链接到测试.

这意味着,库A本身不会编译,因为它缺少接口.这也意味着,如果库C也将使用日志记录工具,它还会引入一个接口ILogger,这将打破ODR?这可以通过引入仅包含接口的额外包层库来解决.但主要问题仍然存在:

在哪里放置界面?我阅读了关于DIP 的原始论文,但我不同意解释,我不应该将接口放入库中.我觉得这篇论文是作为如何思考开发的指导原则(因为"用户正在定义界面而不是实现者").它是否正确?你如何使用依赖倒置原则?

Bér*_*ger 2

软件可以被视为不同层的组合:

\n\n
    \n
  • 一层是实现层(大致就是功能层)

  • \n
  • 另一种是数据结构交互的方式(类级别,这主要是 DIP 应该应用的地方)

  • \n
  • 另一种是组件交互的方式(包层)。如果可能的话,我们也想在这里应用某种 DIP。Robert C. Martin 坚持认为这一层主要依赖于业务(无论这意味着什么),因此原则略有不同:稳定依赖原则和稳定抽象原则(请参阅 Martin 的原则模式和实践)

  • \n
\n\n

现在还应该强调软件工程原理,只有当你必须解决它们解决的问题时才应该应用它们。只要\xe2\x80\x99没有问题,就不要使用它们。

\n\n

在类级别,如果您有充分的理由相信您的日志记录机制将由多个类实现,则应该使用 DIP。如果您认为暂时只有一种日志机制,那么完全可以使用 DIP 也完全没问题,因为没有什么问题需要解决。

\n\n

现在应该在封装级别做出同样的选择。然而,包装选择的指南是部署。这里:

\n\n
class ILogger {\n    virtual void log(const std::string& s) = 0;\n};\nclass A : public ILogger {\n    \xe2\x80\xa6\n};\nclass A2 : public ILogger {\n    \xe2\x80\xa6\n};\n
Run Code Online (Sandbox Code Playgroud)\n\n
    \n
  1. 如果您认为(出于商业原因)在没有 A2 的情况下发布 A 是有意义的,那么创建 4 个库:一个用于 ILogger,一个用于用户类 B,一个用于 A,一个用于 A2。
  2. \n
  3. 如果由于某种原因 A 和 A2 应该一起发布,那么只为 ILogger A 和 A2 制作一个库。如果以后它们应该单独发布,那么就破坏你的库,但不是现在,因为记住:YAGNI
  4. \n
  5. 如果您对 ILogger 只有一种依赖,那么只创建一个包含所有内容的库也是有意义的。
  6. \n
  7. 不要使用 ILogger 和 B 发布一个库,并使用 A 发布另一个库,因为与解决方案 3 相比,您没有任何优势,这更复杂,并且可能进一步违反包的另一项原则:非循环依赖原则。
  8. \n
\n\n

无论如何,这个决定主要取决于业务。还要记住,打包应该自下而上进行:仅当您想要组织许多类时才创建新包。在你没有这么多类之前,不要尝试尽早做出决定,因为你几乎肯定会错。

\n