依赖倒置原则对项目结构有哪些影响? 如果我想使用DIP开发一个假设的模块化C++项目。由于模块化,我选择在一个库A中完全实现一个特定功能。另一个库B(或两个或三个…)正在使用此功能(例如,日志机制):

依赖倒置原则对项目结构有哪些影响? 如果我想使用DIP开发一个假设的模块化C++项目。由于模块化,我选择在一个库A中完全实现一个特定功能。另一个库B(或两个或三个…)正在使用此功能(例如,日志机制):,c++,deployment,dependencies,modularity,dependency-inversion,C++,Deployment,Dependencies,Modularity,Dependency Inversion,我应该把这个界面放在哪里?一些博客作者似乎建议,因为这个界面属于它的用户(因为DIP)(或)。这也将提高可测试性,因为您不需要任何实现来链接到测试 这意味着库A本身不会编译,因为它缺少接口。这还意味着,如果库C也将使用日志记录功能,它还将引入一个接口ILogger,这将破坏数据库?!这可以通过引入一个只包含接口的额外包层库D来解决。但主要问题依然存在: 界面放在哪里?我读了关于DIP的原文,但我不同意这种解释,即我不应该将接口放入库中。我有一种感觉,这篇文章的目的是作为如何思考开发的指导方针(因

我应该把这个界面放在哪里?一些博客作者似乎建议,因为这个界面属于它的用户(因为DIP)(或)。这也将提高可测试性,因为您不需要任何实现来链接到测试

这意味着库A本身不会编译,因为它缺少接口。这还意味着,如果库C也将使用日志记录功能,它还将引入一个接口
ILogger
,这将破坏数据库?!这可以通过引入一个只包含接口的额外包层库D来解决。但主要问题依然存在:


界面放在哪里?我读了关于DIP的原文,但我不同意这种解释,即我不应该将接口放入库中。我有一种感觉,这篇文章的目的是作为如何思考开发的指导方针(因为“用户定义的是接口,而不是实现者”)。这是正确的吗?如何使用依赖倒置原则?

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

  • 一层是实现层(粗略地说,是功能层)

  • 另一个是数据结构相互作用的方式(类级别,这主要是DIP应该应用的地方)

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

现在,软件工程中的原则也应该强调的是,只有当你必须解决它们所解决的问题时,你才应该应用它们。只要你没有问题,就不要使用它们

在类级别,如果有充分的理由相信日志机制将由几个类实现,那么应该使用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创建一个库。如果以后它们应该单独发布,那么请中断您的库,但不是现在,因为请记住:
  • 如果您对ILogger只有一个依赖项,那么只创建一个包含所有内容的库也是有意义的
  • 不要发布一个包含ILogger和B的lib,以及另一个包含a的lib,因为这样与解决方案3相比,您没有优势,这更复杂,而且可能违反包的另一个原则:非循环依赖原则

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

    将接口放在单独的库中怎么样?然后,您可以从A和B中包含该库。最后,建议将其用于DIP…对于大多数用例,这将被夸大。对我来说,最有问题的情况是:如果您将接口与用户打包在一起,并将实现打包在另一个包中,那么它将从包含实现的包向包含用户和接口的包添加构建依赖项。这仅仅是为了获得一个小的可测试性优势而不合理的。答案是Thx。很明显,如果一个接口在不同的库中有两个实现,那么我必须添加一个额外的层,但是DIP似乎坚持认为,如果有多个使用,那么您需要抽象,因为接口属于用户?!您在枚举中的第四个条目似乎违反了我链接的站点的DIP解释?R.C.Martin anywhere是否坚持将界面打包给用户?Martin没有告诉我们要将界面打包给客户。如果您有多个客户机,您将如何打包?因此,在包级别,界面肯定不属于用户。即使在类级别,“属于客户机”实际上意味着“客户机依赖于它”。如果您真的不想依赖于由于ILogger的另一个客户端而可能发生的更改,请使用适配器设计模式创建另一个接口,例如ILogger Forb。由于ILoggerForB是为B创建的,所以它的唯一客户机是B,所以您可以使用B对其进行打包。感谢您的澄清。
    class ILogger {
        virtual void log(const std::string& s) = 0;
    };
    class A : public ILogger {
        …
    };
    class A2 : public ILogger {
        …
    };