什么是依赖倒置原则?为什么它很重要?
当前回答
如果我们可以假定公司的“高级”员工是通过执行他们的计划来获得报酬的,并且这些计划是由许多“低级”员工的计划的综合执行来交付的,那么我们可以说,如果高级员工的计划描述以任何方式与任何低级员工的具体计划耦合在一起,那么这通常是一个糟糕的计划。
If a high level executive has a plan to "improve delivery time", and indicates that an employee in the shipping line must have coffee and do stretches each morning, then that plan is highly coupled and has low cohesion. But if the plan makes no mention of any specific employee, and in fact simply requires "an entity that can perform work is prepared to work", then the plan is loosely coupled and more cohesive: the plans do not overlap and can easily be substituted. Contractors, or robots, can easily replace the employees and the high level's plan remains unchanged.
“高级”在依赖倒置原则中意味着“更重要”。
其他回答
如果我们可以假定公司的“高级”员工是通过执行他们的计划来获得报酬的,并且这些计划是由许多“低级”员工的计划的综合执行来交付的,那么我们可以说,如果高级员工的计划描述以任何方式与任何低级员工的具体计划耦合在一起,那么这通常是一个糟糕的计划。
If a high level executive has a plan to "improve delivery time", and indicates that an employee in the shipping line must have coffee and do stretches each morning, then that plan is highly coupled and has low cohesion. But if the plan makes no mention of any specific employee, and in fact simply requires "an entity that can perform work is prepared to work", then the plan is loosely coupled and more cohesive: the plans do not overlap and can easily be substituted. Contractors, or robots, can easily replace the employees and the high level's plan remains unchanged.
“高级”在依赖倒置原则中意味着“更重要”。
依赖倒置的重点是制作可重用的软件。
其思想是,两段代码不再相互依赖,而是依赖于一些抽象的接口。然后你可以在没有另一块的情况下重复使用其中的任何一块。
最常见的实现方式是通过控制反转(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的依赖项,以及这些依赖项的依赖项,以及…好吧,你懂的。
查看这个文档:依赖倒置原则。
它基本上是说:
高级模块不应该依赖于低级模块。两者都应该依赖于抽象。 抽象永远不应该依赖于细节。细节应该依赖于抽象。
至于为什么它很重要,简而言之:更改是有风险的,通过依赖于概念而不是实现,您减少了在调用站点更改的需求。
DIP有效地减少了不同代码段之间的耦合。其思想是,尽管有许多实现方法,比如日志记录工具,但使用它的方式应该在时间上相对稳定。如果您可以提取一个表示日志记录概念的接口,那么这个接口在时间上应该比它的实现稳定得多,并且调用站点在维护或扩展日志记录机制时受更改的影响应该小得多。
通过使实现依赖于接口,您可以在运行时选择哪个实现更适合您的特定环境。根据具体情况,这可能也很有趣。
依赖倒置的良好应用在应用程序的整个体系结构层面上提供了灵活性和稳定性。它将允许您的应用程序更安全、更稳定地发展。
传统分层建筑
传统上,分层架构UI依赖于业务层,而业务层又依赖于数据访问层。
您必须了解层、包或库。让我们看看代码是怎样的。
我们将有一个用于数据访问层的库或包。
// DataAccessLayer.dll
public class ProductDAO {
}
以及依赖于数据访问层的另一个库或包层业务逻辑。
// BusinessLogicLayer.dll
using DataAccessLayer;
public class ProductBO {
private ProductDAO productDAO;
}
带有依赖倒置的分层体系结构
依赖倒置表明:
高级模块不应该依赖于低级模块。两者都应该依赖于抽象。
抽象不应该依赖于细节。细节应该依赖于抽象。
什么是高级模块和低级模块?思考模块,如库或包,高级模块将是那些传统上具有依赖关系的模块,而它们所依赖的低级模块。
换句话说,模块高层是调用操作的地方,而模块低层是执行操作的地方。
从这一原则可以得出一个合理的结论,即具象之间不应存在依赖关系,而必须对抽象存在依赖关系。但根据我们所采取的方法,我们可能会误用投资依赖依赖,而是一种抽象。
假设我们对代码进行如下调整:
我们将为定义抽象的数据访问层提供一个库或包。
// DataAccessLayer.dll
public interface IProductDAO
public class ProductDAO : IProductDAO{
}
以及依赖于数据访问层的另一个库或包层业务逻辑。
// BusinessLogicLayer.dll
using DataAccessLayer;
public class ProductBO {
private IProductDAO productDAO;
}
尽管我们依赖于抽象,但业务和数据访问之间的依赖关系保持不变。
要获得依赖项反转,持久性接口必须定义在高级逻辑或域所在的模块或包中,而不是定义在低级模块中。
首先定义什么是领域层,其通信的抽象是定义持久化。
// Domain.dll
public interface IProductRepository;
using DataAccessLayer;
public class ProductBO {
private IProductRepository productRepository;
}
在持久化层依赖于域之后,如果定义了依赖项,就可以进行反转。
// Persistence.dll
public class ProductDAO : IProductRepository{
}
(来源:xurxodev.com)
深化原则
重要的是要吸收好概念,深化目的和效益。如果我们机械地停留在学习典型案例存储库中,我们将无法确定在哪里可以应用依赖原则。
但是为什么要反转依赖关系呢?除了具体的例子,主要目标是什么?
这通常允许最稳定的东西,不依赖于不太稳定的东西,更频繁地变化。
持久化类型的更改(访问相同数据库的数据库或技术)比设计用于与持久化通信的域逻辑或操作更容易。因此,这种依赖是相反的,因为如果发生这种更改,则更容易更改持久性。这样我们就不需要改变定义域了。域层是所有层中最稳定的,这就是为什么它不应该依赖于任何东西。
但不只是这个存储库示例。有许多应用此原则的场景,并且有基于此原则的架构。
体系结构
在某些架构中,依赖倒置是其定义的关键。在所有的域中,它是最重要的,它是用来指示域和其他被定义的包或库之间的通信协议的抽象。
干净的建筑
在Clean体系结构中,域位于中心,如果您查看指示依赖关系的箭头方向,就可以清楚地看到哪些是最重要和最稳定的层。外层被认为是不稳定的工具,因此避免依赖它们。
(来源:8 thlight.com)
六角结构
这与六边形体系结构发生的情况相同,其中域也位于中心部分,端口是从多米诺骨牌向外通信的抽象。这里很明显,域是最稳定的,传统的依赖关系是颠倒的。
(来源:pragprog.com)
依赖倒置:依赖抽象,而不是具体。
控制反转:主要与抽象,以及主要如何成为系统的粘合剂。
以下是一些关于这方面的好文章:
https://coderstower.com/2019/03/26/dependency-inversion-why-you-shouldnt-avoid-it/
https://coderstower.com/2019/04/02/main-and-abstraction-the-decoupled-peers/
https://coderstower.com/2019/04/09/inversion-of-control-putting-all-together/
推荐文章
- 什么是依赖倒置原则?为什么它很重要?
- 为什么在Python方法中需要显式地有“self”参数?
- 如何在方法中访问“静态”类变量?
- 为什么c#不提供c++风格的'friend'关键字?
- String, StringBuffer和StringBuilder
- 存储库和服务层的区别?
- DDD -实体不能直接访问存储库的规则
- 为什么STL如此严重地基于模板而不是继承?
- 如何在Objective-C中声明类级属性?
- 面向方面编程与面向对象编程
- c++中类似于java的instanceof
- 在python中遍历对象属性
- 将类代码分离为头文件和cpp文件
- 在PHP中使用getter和setter而不是函数或简单的公共字段有什么优点?
- 基于原型的继承与基于类的继承