语言-编程原则SOLID-DIP

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.

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).

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

 

动机

当我们设计软件应用程序时,我们可以考虑实现基本和主要操作的类(磁盘访问,网络协议...)和高级类(它们封装复杂逻辑(业务流,...))的低级类。最后的依赖于低级别的类。实现这种结构的一种自然方式是编写低级别类,一旦我们让他们编写复杂的高级类。由于高级别类别是以其他方式定义的,因此这似乎是合乎逻辑的方式。但这不是一个灵活的设计。如果我们需要更换低级别的类,会发生什么?

我们来看一个复制模块的经典示例,它从键盘读取字符并将它们写入打印机设备。包含逻辑的高级类是Copy类。低级别类是KeyboardReader和PrinterWriter。

在糟糕的设计中,高级类直接使用并且严重依赖于低级类。在这种情况下,如果我们想改变设计以将输出引导到新的FileWriter类,我们必须对Copy类进行更改。 (让我们假设它是一个非常复杂的类,有很多逻辑,很难测试)。

为了避免这些问题,我们可以在高级类和低级类之间引入一个抽象层。由于高级模块包含复杂的逻辑,因此它们不应该依赖于低级模块,因此不应该基于低级模块创建新的抽象层。低级模块将基于抽象层创建。

根据这个原则,设计一个类结构的方法是从高级模块开始到低级模块:

高级类 - >抽象层 - >低级类

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.

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:

·         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.

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.

 

下面是一个违反依赖倒置原则的例子。我们有一个高层次的经理类和一个名为Worker的低层类。我们需要在我们的应用程序中添加一个新模块,以模拟由新专业人员的雇佣决定的公司结构变化。我们为此创建了一个新类SuperWorker。

 

假设Manager类非常复杂,包含非常复杂的逻辑。现在我们必须改变它以引入新的SuperWorker。让我们看看缺点:

我们必须改变Manager类(记住它是一个复杂的类,这需要花费时间和精力进行更改)。

管理员类中的一些当前功能可能会受到影响。

单元测试应该重做。

所有这些问题都需要很长时间才能解决,并且可能会在旧功能中引发新的错误。如果应用程序是按照依赖性倒置原则设计的,情况会有所不同。这意味着我们设计了管理类,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
               }
}

 

// 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.

The Template Design Pattern is an example where the DIP principle is applied.

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.

 

结论

当应用这个原理时,它意味着高级类不直接与低级类一起工作,它们将接口用作抽象层。 在这种情况下,使用new操作符无法完成高级类内的新低级对象的实例化(如有必要)。 相反,可以使用一些Creational设计模式,例如Factory Method,Abstract Factory,Prototype。

 

模板设计模式是应用DIP原理的一个例子。

 

当然,使用这个原则意味着更多的努力,会导致更多的类和接口需要更复杂的代码来维护,但更灵活。 这个原则不应该被盲目地应用于每个类或每个模块。 如果我们有一个更可能在未来保持不变的类功能,则不需要应用这个原则。

 一句话总结:

 该原则,本质是在基础类之上添加了一个抽象接口,就像最后作者给出的:如果这样做会导致更多的类和接口需求需要维护,但是更灵活,不应该被盲目使用,需要结合具体的业务来确定。

 

 

 

 

 

 


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值