面向对象的原则之依赖倒置原则

124 篇文章 139 订阅
13 篇文章 0 订阅

依赖倒置原则

  什么是依赖倒置呢?简单地讲就是将依赖关系倒置为依赖接口,具体概念如下:

  • 上层模块不应该依赖于下层模块,它们共同依赖于一个抽象(父类不能依赖子类,它们都要依赖抽象类)。

  • 抽象不能依赖于具体,具体应该要依赖于抽象。

      注意,这里的接口不是狭义的接口。

      为什么要依赖接口?因为接口体现对问题的抽象,同时由于抽象一般是相对稳定的或者是相对变化不频繁的,而具体是易变的。因此,依赖抽象是实现代码扩展和运行期内绑定(多态)的基础:只要实现了该抽象类的子类,都可以被类的使用者使用。这里,我想强调一下扩展性这个概念。通常扩展性是指对已知行为的扩展,在讲述接口那一节,我也提到,接口应该是相对稳定的。这就告诉我们,无论使用多么先进的设计模式,也无法做到不需要修改代码即可达到以不变应万变的地步。在面向对象的这五大原则里,我认为依赖倒置是最难理解,也是最难实现的。

      这个例子以前面提到的雇员类为蓝本,实现代码如代码如下:

    <?php 
    interface employee{  
    public function working();  
    }  
    class teacher implements employee{  
    public function working(){  
    echo 'teaching...';  
    }  
    }  
    class coder implements employee{  
    public function working(){  
    echo 'coding...';  
    }  
    }  
    class workA{  
    public function work(){  
    $teacher=new teacher;  
    $teacher->working();  
    }  
    }  
    class workB{  
    private $e;  
    public function set(employee $e){  
    $this->e=$e;  
    }  
    public function work(){  
    $this->e->working();  
    }  

    }  
    $worka=new workA;  
    $worka->work();  
    $workb=new workB;  
    $workb->set(new teacher());  
    $workb->work(); 

  在classA中,work方法依赖于teacher实现;在classB中,work转而依赖于抽象,这样可以把需要的对象通过参数传入。上述代码通过接口,实现了一定程度的解耦,但仍然是有限的。不仅是使用接口,使用工厂等也能实现一定程度的解耦和依赖倒置。

  在workB中,teacher实例通过setter方法传入中,从而实现了工厂模式。由于这样的实现仍然是硬编码的,为了实现代码的进一步扩展,把这个依赖关系写在配置文件里,指明classB需要一个teacher对象,专门由一个程序检测配置是否正确(如所依赖的类文件是否存在)以及加载配置中所依赖的实现,这个检测程序,就称为IOC容器。

  很多文章里看到IOC(Inversion of Control)概念,实际上,IOC是依赖倒置原则(Dependence Inversion Principle,DIP)的同义词。而在提IOC的时候,你可能还会看到有人提起DI等概念。DI,即依赖注入,一般认为,依赖注入(DI)和依赖查找(DS)是IOC的两种实现。不过随着某些概念的演化,这几个概念之间的关系也变得很模糊,也有人认为IOC就是DI。有人认为,依赖注入的描述比起IOC来更贴切,这里不纠缠于这几个概念之间的关系。

  在经典的J2EE设计里,通常把DAO层和Service层细分为接口层和实现层,然后在配置文件里进行依赖关系的配置,这是最常见的DIP的应用。Spring框架就是一个很好的IOC容器,把控制权从代码剥离到IOC容器,这里是通过XML配置文件实现的,Spring在执行时期根据配置文件的设定,建立对象之间的依赖关系。

  如下面代码所示:

    <bean scope="prototype" 
    class="cn.notebook.action.NotebookListOtherAction" 
    id="notebookListOtherAction"> 
    <property ref="userReplyService" name="userReplyService"/> 
    <property ref="userService" name="userService"/> 
    <property ref="permissionService" name="permissionService"/> 
    <property ref="friendService" name="friendService"/> 
    </bean> 

  但是这样设置一样存在问题,配置文件会变得越来越大,其间关系会越来越复杂。同样逃脱不了随着应用和业务的改变,不断修改代码的恶魇(这里认为配置文件是代码的一部分。并且在实际开发中,很少存在单纯修改配置文件的情况。一般配置文件修改了,代码也会做相应修改)。

  在PHP里,也有类似模仿Spring的实现,即把依赖关系写在了配置文件里,通过配置文件来产生需要的对象。我觉得这样的代码是还是为了实现而实现。在Spring里,配置文件里配置的不仅仅是一个类运行时的依赖关系,还可以实现事务管理、AOP、延迟加载等。而PHP要实现上面的种种特性,其消耗是巨大的。从语言层面讲,PHP这种动态脚本型语言在实现一些多态特性上和编译型的语言不同。其次PHP作为敏捷性的开发语言,更强调快速开发、逻辑清晰、代码简单易懂,如果再附加了各种设计模式的框架,从技术实现和运行效率上来看,都是不可取的。依赖倒置的核心原则是解耦。如果脱离这个最原始的原则,那就是本末倒置。

  事实上,很多的设计模式里已经隐含了依赖倒置原则,我们也在有意或无意地做着一些依赖反转的工作。只是作为PHP,目前还没有一个比较完善的IOC容器,或许是PHP根本不需要。

  如何满足DIP:

  • 每个较高层次类都为它所需要的服务提出一个接口声明,较低层次类实现这个接口。
  • 每个高层类都通过该抽象接口使用服务。
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值