依赖倒置原则(DIP)

php基础

浏览数:199

2019-9-6

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

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

<?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();

在workA中,work方法依赖于teacher实现;在workB中,work转而依赖抽象,这样可以把需要的对象通过参数传入。上述代码通过接口,实现了一定程度的解耦,但仍然是有限的。不仅是使用接口,使用工厂等也能实现一定程度的解耦和依赖倒置。 在workB中,teacher实例通过set方法传入,从而实现了工厂模式。由于这样的实现仍然是硬编码的,为了实现代码的进一步扩展,把这个依赖关系写在配置文件里,指明workB需要一个teacher对象,专门由一个程序配置是否正确(如所依赖的类文件是否存在)以及加载配置中所依赖的实现,这个检测程序,就称为IOC容器。 很多文章里看到IOC(Inversion of Control)概念,实际上,IOC是依赖倒置原则(Dependence Inversion Principle,DIP)的同义词。而在提IOC的时候,你可能还会看到有人提起DI等概念。DI,即依赖注入,一般认为,依赖注入(DI)和依赖查找(DS)是IOC的两种实现。不过随着某些概论的演化,这几个概念之间的关系也变得很模糊,也有人认为IOC就是DI。有人认为,依赖注入的描述比起IOC来更贴切,这里不纠缠于这几个概念之间的关系。 在经典的J2EE设计里,通常把DAO层和Servicen层细分为接口层和实现层,然后在配置文件里进行所依赖关系的配置,这是最常见的DIP的应用。Spring框架就是一个很好的IOC容器,把控制权从代码剥离到IOC窗口,这里是通过XML配置文件实现的,Spring在执行期间根据配置文件的设定,建立对象之间的依赖关系。 如下面的代码所示

<bean scopre="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的实现,即把依赖关系写在了配置文件里,通过配置文件来产生需要的对象。我觉得这样的代码还是为了实现而实现。在Srping里,配置文件里配置的不仅仅是一个类运行时的依赖关系,还可以实现事务管理、AOP、延迟加载等。而PHP要实现上面的种种特性,其消耗是巨大的。从语言层面讲,PHP这种动态脚本语言在实现一些多态特性上和编译型的语言不同。其次PHP作为敏捷性的开发语言,更强调快速开发、逻辑清晰、代码更简单易懂,如果再附加了各种设计模式的框架,从技术实现和运行效率上来看,都是不可取的。依赖倒置的核心原则是解耦。如果脱离这个最原始的原则,那就是本末倒置。     事实上,很多的设计模式里已经隐含了依赖倒置原则我们也在有意无意地做着一些依赖反转的工作。只是作为PHP,目前还没有一个比较完善的IOC容器,或许是PHP根本不需要。 如果满足DIP: 1.每个较高层次类都为它所需要的服务提出一个接口声明,较低层次类实现实现这个接口。 2.每个高层次类都通过该抽象接口使用服务。

作者:雪山飞猪