[设计模式之禅读书笔记]003_设计模式六大原则(三):依赖倒置原则(Dependence Inversion Principle)

序言

   依赖倒置,这个概念看起来很玄乎,其实很简单。这也是我看所有技术书的心态,在心态上战胜这本书,那么它的内容,也就能很容易理解了。依赖倒置的英文定义如下:

   High level modules should not depend upon low level modules. Both should depend upon abstractions. Abstractions should not depend upon details. Details should depend upon abstractions。

   英文不给力的自动跳过吧。这个原则的意思就是,代码要依赖于抽象,而不依赖于实现。这又是什么意思呢?我有个比喻:公司需要你吗?需要,这叫依赖。但是你觉得你所在的公司离开你就不行了吗?显然不是的,公司只是依赖于具有拥有技能的人。所以,这里的拥有技能的人是你和你同事的抽象,当然也包括社会上的人。那所谓的倒置是什么呢?公司如果依赖你,那就叫正置;但是公司现在依赖的是具有技能的人,是抽象集合体,这就叫倒置。所谓正置是指依赖于具体的实现,而倒置则是依赖于对具体的抽象。

正文

   1. 依赖倒置有什么好处呢?

       依赖倒置原则的要求是依赖于抽象,那么这个问题就相当于在问,基于抽象编程有什么好处呢?这样问题就容易回答了。抽象是什么?抽象是将一群具有相似特征和行为的个体归为一类。这样,我就不用为每一个个体实现的类定制函数了。试想,加入我有一个函数名叫“杀鸡”,它的参数不是抽象的鸡,而是土鸡,那么当我要杀饲料鸡的时候,还需要将“杀鸡”重载一个参数为饲料鸡的函数。这样会十分麻烦。所以,依赖倒置的好处之一是:

不必为某一具体个体定制特别的处理函数,可以避免大量的无趣的函数重载。

       那还有其他好处吗?当然有了,依赖倒置可以降低具体类之间的耦合性。这是什么意思呢?看下面一段不好的程序:

 1 class QiRuiQQ{
 2 public:
 3     void run(){
 4         cout<<"QiRuiQQ running..."<<endl;
 5     }
 6 };
 7 
 8 class Driver{
 9 public:
10     void drive(Benz bz){
11         bz.run();
12     }
13 };
14 
15 int main(){
16 
17     Driver *d = new Driver();
18     QiRuiQQ *q = new QiRuiQQ();
19     d->drive(*q);
20 
21     delete q;
22     delete d;
23 
24     return 0;
25 }

      这两个类的耦合度如何呢?可以告诉你,太高了。为什么呢?试想,假如Driver要开法拉利怎么办?我现在有一个法拉利的类:

1 class Ferrari{
2 public:
3     void run(){
4         cout<<"Ferrari running..."<<endl;
5     }
6 };

      那Driver只能眼睁睁的看着这样一辆超级跑车,他却开不走。如果要让Driver能开法拉利,我们就需要为Drive加一个重载函数,而这函数体于QiRuiQQ的drive方法惊人的相似。那么我们怎么办呢?这个时候就需要我们伟大的依赖倒置原则了,看符合依赖倒置的代码吧:

 1 /**
 2  *车辆的抽象
 3  **/
 4 class ICar{
 5 public:
 6     virtual void run(){}
 7 };
 8 /**
 9  *司机的抽象
10  **/
11 class IDriver{
12 public:
13     virtual void drive(ICar car){}
14 };
15 /**
16  *车辆的具象:奇瑞QQ
17  **/
18 class QiRuiQQ:public ICar{
19 public:
20     void run(){
21         cout<<"QiRuiQQ running..."<<endl;
22     }
23 };
24 /**
25  *车辆的具象:法拉利
26  **/
27 class Ferrari:public ICar{
28 public:
29     void run(){
30         cout<<"Ferrari running..."<<endl;
31     }
32 };
33 /**
34  *司机的具象
35  **/
36 class Driver:public IDriver{
37 public:
38     void drive(ICar* car){
39         car->run();
40     }
41 };
42 
43 int main(){
44 
45     Driver *d = new Driver();
46     QiRuiQQ *q = new QiRuiQQ();
47     d->drive(q);
48 
49     delete q;
50     delete d;
51     system("pause");
52     return 0;
53 }

运行结果:

   QiRuiQQ running...
   请按任意键继续. . .

 

   此时无论你给司机什么车,司机都会开了,只要那个车继承了ICar。这样就降低了两个类别的耦合度了。所以,依赖倒置的第二个好处就出来了:

降低类间的耦合度。

   2. 依赖的实现方法有哪些?

       所谓的依赖实现,这个概念貌似很牛逼,其实很简单的,就是怎么把参数传给另一个类。作者给了三种方法,我们来看看到底是哪三种吧。

         ◇a. 构造函数(构造对象的时候传入另一个对象)

         ◇b. setter方法(Java里有Bean的概念,C++也可以完全模仿啊!其实就是一个设置成员属性的值的函数而已)

         ◇c. 接口声明依赖对象(就是我们上面的代码了)

总结

   1. 依赖倒置原则可以避免大量的函数重载。

   2. 依赖倒置原则可以降低类间的耦合度。

   3. 依赖的实现方法有三种,分别是:构造函数、setter方法、接口声明依赖对象。

注意

   依赖倒置虽然给我们提供了方便之门,但是从上面的代码也可以看出,它很容易类的规模膨胀,如果为每一个类都声明一个接口,那就会膨胀的可怕。所以,对于我们这些技术人来说,在技术的应用上,要有拿捏,不能没有,也不能全都是。

posted @ 2012-10-23 23:47  邵贤军  阅读(1900)  评论(5编辑  收藏  举报