依赖项反转原理是什么,为什么重要?
依赖项反转原理是什么,为什么重要?
解决方案
回答
签出此文档:依赖倒置原则。
它基本上说:
- 高级模块不应依赖于低级模块。两者都应依赖抽象。
- 抽象永远不要依赖细节。细节应取决于抽象。
简而言之,为何重要:变更是有风险的,并且通过依赖概念而不是实施来减少呼叫站点的变更需求。
有效地,DIP减少了不同代码段之间的耦合。这个想法是,尽管有许多实现日志工具的方法,但是我们使用它的方法应该在时间上相对稳定。如果我们可以提取一个表示日志记录概念的接口,则该接口在时间上应比其实现稳定得多,并且呼叫站点受保持或者扩展该日志记录机制时所做的更改的影响应小得多。
通过使实现也依赖于接口,我们可以在运行时选择哪种实现更适合特定环境。根据情况,这也可能很有趣。
回答
依赖性反转的重点是使软件可重用。
这个想法是,它们依赖于一些抽象的接口,而不是两个相互依赖的代码。然后,我们可以重用任何一块,而无需另一块。
最常见的实现方法是通过控制反转(IoC)容器(如Java中的Spring)来实现。在此模型中,对象的属性是通过XML配置设置的,而不是通过退出对象并找到它们的依赖关系来设置的。
想象一下这个伪代码...
public class MyClass { public Service myService = ServiceLocator.service; }
MyClass直接依赖于Service类和ServiceLocator类。如果要在另一个应用程序中使用它,则需要这两个条件。现在想象一下...
public class MyClass { public IService myService; }
现在,MyClass依赖于一个接口,即IService接口。我们让IoC容器实际设置该变量的值。
因此,现在,MyClass可以轻松地在其他项目中重用,而无需带来其他两个类的依赖。
更好的是,我们不必拖动MyService的依赖关系,这些依赖关系的依赖关系,以及……嗯,我们就明白了。
回答
Martin Fowler的Control Containers的反转和Dependency Injection模式也是一本好书。我发现Head First Design Patterns是一本很棒的书,这是我第一次尝试学习DI和其他模式。
回答
控制反转(IoC)是一种设计模式,在这种模式下,对象由外部框架处理其依赖关系,而不是向框架询问其依赖关系。
使用传统查找的伪代码示例:
class Service { Database database; init() { database = FrameworkSingleton.getService("database"); } }
使用IoC的类似代码:
class Service { Database database; init(database) { this.database = database; } }
IoC的好处是:
- 我们不依赖中央框架,因此可以根据需要更改。
- 由于对象是通过注入(最好使用接口)创建的,因此很容易创建将依赖项替换为模拟版本的单元测试。
- 解耦代码。
回答
这里的其他人已经给出了好的答案和好的例子。
DIP之所以重要很重要,是因为它确保了面向对象原理的"松耦合设计"。
软件中的对象不应进入层次结构,其中某些对象是顶级对象,这取决于低级对象。然后,低级对象的更改将波及到顶级对象,这使软件对于更改非常脆弱。
我们希望"顶级"对象非常稳定,并且不易更改,因此我们需要反转依赖关系。
回答
书籍《敏捷软件开发,原理,模式和实践》以及《敏捷护理中的原理,模式和实践》是最好的资源,可以充分理解依赖反转原理背后的原始目标和动机。 "依赖倒置原则"一文也是一个很好的参考资料,但是由于它是草案的精简版,最终被引入到前面提到的书中,因此省略了一些有关"概念"的概念的重要讨论。包和接口所有权是区分此原理与更常见的建议的关键,建议在"设计模式"一书(Gamma等)中找到"对接口进行编程,而不是对实现进行编程"。
为了提供一个摘要,依赖性反转原理主要是将依赖关系的常规方向从"较高级别"组件转换为"较低级别"组件,以便"较低级别"组件依赖于"较高级别"组件拥有的接口。 (注意:"高级"组件在这里是指需要外部依赖项/服务的组件,而不一定是其在分层体系结构中的概念位置。)这样做时,耦合并没有减少太多,因为它从理论上的组件上转移了很多对重用而言价值较小的组件,这些组件在理论上对重用价值较高。
这是通过设计组件来实现的,这些组件的外部依赖性通过接口来表示,组件的使用者必须为其提供实现。换句话说,定义的接口表示组件所需的内容,而不是组件的使用方式(例如," INeedSomething",而不是" IDoSomething")。
依赖关系反转原理所指的不是通过使用接口(例如MyService [ILogger?Logger])抽象依赖关系的简单实践。尽管这将组件与依赖项的特定实现细节分离开来,但它不会反转使用者与依赖项之间的关系(例如[MyService IMyServiceLogger]?Logger。
依赖倒置原则的重要性主要体现在依赖外部依赖项(日志记录,验证等)的可重用软件组件的开发中,因为对此类依赖项进行依赖也需要使用者也需要相同的依赖项。当库的使用者选择使用其他库来满足相同的基础结构需求(例如NLog与log4net)时,或者如果他们选择使用与该版本不向后兼容的所需库的更高版本,这可能会出现问题。图书馆要求的。
在此可以找到有关此原理的更长时间的讨论,因为它涉及接口的简单使用,依赖注入和分离的接口模式。
回答
对我来说,如官方文章中所述,依赖倒置原则确实是一种错误的尝试,它试图提高本质上不可重用的模块的可重用性,并且是解决C ++语言中问题的一种方法。
C ++中的问题是头文件通常包含私有字段和方法的声明。因此,如果高级C ++模块包括低级模块的头文件,则它将取决于该模块的实际实现细节。显然,这不是一件好事。但这在当今常用的更现代的语言中不是问题。
高级模块本质上比低级模块具有较低的可重用性,因为前者通常比后者具有更多的应用程序/上下文特定性。例如,实现UI屏幕的组件是最高级别的,并且也非常(完全?)特定于该应用程序。尝试在不同的应用程序中重用此类组件会适得其反,只会导致过度设计。
因此,只有在组件A对于在不同应用程序或者上下文中重用确实有用的情况下,才能在依赖于组件B(不依赖于A)的组件A的同一级别上创建单独的抽象。如果不是这种情况,那么应用DIP将是错误的设计。