依赖倒置原则对项目结构有哪些影响? 如果我想使用DIP开发一个假设的模块化C++项目。由于模块化,我选择在一个库A中完全实现一个特定功能。另一个库B(或两个或三个…)正在使用此功能(例如,日志机制):
我应该把这个界面放在哪里?一些博客作者似乎建议,因为这个界面属于它的用户(因为DIP)(或)。这也将提高可测试性,因为您不需要任何实现来链接到测试 这意味着库A本身不会编译,因为它缺少接口。这还意味着,如果库C也将使用日志记录功能,它还将引入一个接口依赖倒置原则对项目结构有哪些影响? 如果我想使用DIP开发一个假设的模块化C++项目。由于模块化,我选择在一个库A中完全实现一个特定功能。另一个库B(或两个或三个…)正在使用此功能(例如,日志机制):,c++,deployment,dependencies,modularity,dependency-inversion,C++,Deployment,Dependencies,Modularity,Dependency Inversion,我应该把这个界面放在哪里?一些博客作者似乎建议,因为这个界面属于它的用户(因为DIP)(或)。这也将提高可测试性,因为您不需要任何实现来链接到测试 这意味着库A本身不会编译,因为它缺少接口。这还意味着,如果库C也将使用日志记录功能,它还将引入一个接口ILogger,这将破坏数据库?!这可以通过引入一个只包含接口的额外包层库D来解决。但主要问题依然存在: 界面放在哪里?我读了关于DIP的原文,但我不同意这种解释,即我不应该将接口放入库中。我有一种感觉,这篇文章的目的是作为如何思考开发的指导方针(因
ILogger
,这将破坏数据库?!这可以通过引入一个只包含接口的额外包层库D来解决。但主要问题依然存在:
界面放在哪里?我读了关于DIP的原文,但我不同意这种解释,即我不应该将接口放入库中。我有一种感觉,这篇文章的目的是作为如何思考开发的指导方针(因为“用户定义的是接口,而不是实现者”)。这是正确的吗?如何使用依赖倒置原则?软件可以被视为不同层的组合:
- 一层是实现层(粗略地说,是功能层)
- 另一个是数据结构相互作用的方式(类级别,这主要是DIP应该应用的地方)
- 另一个是组件之间交互的方式(包层)。如果可能的话,我们也希望在这里使用某种类型的DIP。Robert C.Martin坚持认为这一层主要依赖于业务(无论这意味着什么),因此原则有点不同:稳定依赖原则和稳定抽象原则(参见Martin的原则模式和实践)
class ILogger {
virtual void log(const std::string& s) = 0;
};
class A : public ILogger {
…
};
class A2 : public ILogger {
…
};
无论如何,这一决定主要取决于业务。还要记住,打包应该是自下而上的:只有当您有许多要组织的类时,才创建一个新的包。在您没有这么多类之前,不要尝试提前做出决定,因为您几乎肯定会错。将接口放在单独的库中怎么样?然后,您可以从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 {
…
};