原文链接:Dependency Inversion Principle
Motivation
动机
When we design software applications we can consider the low level classes the classes which implement basic and primary operations(disk access, network protocols,...) and high level classes the classes which encapsulate complex logic(business flows, ...). The last ones rely on the low level classes. A natural way of implementing such structures would be to write low level classes and once we have them to write the complex high level classes. Since high level classes are defined in terms of others this seems the logical way to do it. But this is not a flexible design. What happens if we need to replace a low level class?
当我们设计软件应用程序时,我们可以认为低级别类实现基本和主要操作(磁盘访问,网络协议......)和高级类,这些类封装了复杂的逻辑(业务流,......)。 最后一个依赖于低级别的课程。 实现这种结构的一种自然方式是编写低级类,一旦我们让它们编写复杂的高级类。 由于高级类是根据其他类别定义的,因此这似乎是合乎逻辑的方法。 但这不是一个灵活的设计。 如果我们需要更换低级别课程会怎样?
Let's take the classical example of a copy module which reads characters from the keyboard and writes them to the printer device. The high level class containing the logic is the Copy class. The low level classes are KeyboardReader and PrinterWriter.
我们来看一个复制模块的经典例子,它从键盘读取字符并将它们写入打印机设备。 包含逻辑的高级类是Copy类。 低级类是KeyboardReader和PrinterWriter。
In a bad design the high level class uses directly and depends heavily on the low level classes. In such a case if we want to change the design to direct the output to a new FileWriter class we have to make changes in the Copy class. (Let's assume that it is a very complex class, with a lot of logic and really hard to test).
在糟糕的设计中,高级类直接使用并且在很大程度上依赖于低级类。 在这种情况下,如果我们想要更改设计以将输出定向到新的FileWriter类,我们必须在Copy类中进行更改。 (我们假设它是一个非常复杂的类,有很多逻辑并且很难测试)。
In order to avoid such problems we can introduce an abstraction layer between high level classes and low level classes. Since the high level modules contain the complex logic they should not depend on the low level modules so the new abstraction layer should not be created based on low level modules. Low level modules are to be created based on the abstraction layer.
为了避免这些问题,我们可以在高级类和低级类之间引入一个抽象层。 由于高级模块包含复杂逻辑,因此它们不应依赖于低级模块,因此不应基于低级模块创建新的抽象层。 基于抽象层创建低级模块。
According to this principle the way of designing a class structure is to start from high level modules to the low level modules:
High Level Classes --> Abstraction Layer --> Low Level Classes
根据这个原则,设计类结构的方法是从高级模块开始到低级模块:
高级类 - >抽象层 - >低级类
Intent
意图
High-level modules should not depend on low-level modules. Both should depend on abstractions.
Abstractions should not depend on details. Details should depend on abstractions.
高级模块不应该依赖于低级模块。 两者都应该取决于抽象。
抽象不应取决于细节。 细节应取决于抽象。
Example
例子
Below is an example which violates the Dependency Inversion Principle. We have the manager class which is a high level class, and the low level class called Worker. We need to add a new module to our application to model the changes in the company structure determined by the employment of new specialized workers. We created a new class SuperWorker for this.
以下是违反依赖性倒置原则的示例。 我们有一个高级类的经理类,以及名为Worker的低级类。 我们需要在我们的应用程序中添加一个新模块,以模拟公司结构的变化,这些变化取决于新专业人员的雇用。 我们为此创建了一个新的类SuperWorker。
Let's assume the Manager class is quite complex, containing very complex logic. And now we have to change it in order to introduce the new SuperWorker. Let's see the disadvantages:
我们假设Manager类非常复杂,包含非常复杂的逻辑。 现在我们必须改变它才能引入新的SuperWorker。 让我们看看缺点:
we have to change the Manager class (remember it is a complex one and this will involve time and effort to make the changes).
some of the current functionality from the manager class might be affected.
the unit testing should be redone.
我们必须更改Manager类(记住它是一个复杂的类,这将需要时间和精力来进行更改)。
管理器类中的某些当前功能可能会受到影响。
应重新进行单元测试。
All those problems could take a lot of time to be solved and they might induce new errors in the old functionlity. The situation would be different if the application had been designed following the Dependency Inversion Principle. It means we design the manager class, an IWorker interface and the Worker class implementing the IWorker interface. When we need to add the SuperWorker class all we have to do is implement the IWorker interface for it. No additional changes in the existing classes.
所有这些问题可能需要花费大量时间才能解决,并且它们可能会在旧功能中引发新的错误。 如果应用程序是根据依赖性倒置原则设计的,情况会有所不同。 这意味着我们设计了管理器类,IWorker接口和实现IWorker接口的Worker类。 当我们需要添加SuperWorker类时,我们所要做的就是为它实现IWorker接口。 现有类中没有其他更改。
// Dependency Inversion Principle - Bad example
class Worker {
public void work() {
// ....working
}
}
class Manager {
Worker worker;
public void setWorker(Worker w) {
worker = w;
}
public void manage() {
worker.work();
}
}
class SuperWorker {
public void work() {
//.... working much more
}
}
Below is the code which supports the Dependency Inversion Principle. In this new design a new abstraction layer is added through the IWorker Interface. Now the problems from the above code are solved(considering there is no change in the high level logic):
下面是支持依赖性倒置原则的代码。 在这个新设计中,通过IWorker接口添加了一个新的抽象层。 现在解决了上述代码中的问题(考虑到高级逻辑没有变化):
Manager class doesn't require changes when adding SuperWorkers.
Minimized risk to affect old functionality present in Manager class since we don't change it.
No need to redo the unit testing for Manager class.
添加SuperWorkers时,Manager类不需要更改。
最小化影响Manager类中存在的旧功能的风险,因为我们不更改它。
无需重做Manager类的单元测试。
// Dependency Inversion Principle - Good example
interface IWorker {
public void work();
}
class Worker implements IWorker{
public void work() {
// ....working
}
}
class SuperWorker implements IWorker{
public void work() {
//.... working much more
}
}
class Manager {
IWorker worker;
public void setWorker(IWorker w) {
worker = w;
}
public void manage() {
worker.work();
}
}
Conclusion
总结
When this principle is applied it means the high level classes are not working directly with low level classes, they are using interfaces as an abstract layer. In this case instantiation of new low level objects inside the high level classes(if necessary) can not be done using the operator new. Instead, some of the Creational design patterns can be used, such as Factory Method, Abstract Factory, Prototype.
当应用这个原则时,它意味着高级类不能直接使用低级类,它们使用接口作为抽象层。 在这种情况下,不能在高级类(如果需要)内实例化新的低级对象。 相反,可以使用一些Creational设计模式,例如Factory Method,Abstract Factory,Prototype。
The Template Design Pattern is an example where the DIP principle is applied.
模板设计模式是应用DIP原理的示例。
Of course, using this principle implies an increased effort, will result in more classes and interfaces to maintain, in a few words in more complex code, but more flexible. This principle should not be applied blindly for every class or every module. If we have a class functionality that is more likely to remain unchanged in the future there is not need to apply this principle.
当然,使用这个原则意味着增加了工作量,将导致更多的类和接口维护,用更简单的代码表示,但更灵活。 这个原则不应盲目地适用于每个类或每个模块。 如果我们的类功能在未来更有可能保持不变,则不需要应用此原则。