1.原文: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。
2.直译:高水平(层次)的模块不应该依赖底水平(层次)的模块,两者都应该依赖抽象。抽象不应该依赖具体实现,具体实现应该依赖抽象。
3.理解:
(1)有依赖关系的两个类如果有层次关系,比如业务类->基础类,这时不能直接调用.应该面对一个抽象编程,业务类调用这个抽象类.
(2)有实现关系的两个类,抽象类(接口也算)->实现类,抽象类不能依赖一个具体类. 如果是一个独立的实现类没有抽象意义的,也应该依赖抽象
来段儿代码吧.我想了半天确实是有意义的.
<?php
//实现一个发布功能
/*
抽象类:发布
*/
Abstract class APublish{
abstract function doSomething(Wx $content);
}
/*
实现类:发布
*/
class Publish{
function doSomething(Wx $channelContent){
//... 发布相关的功能
echo '统一加上公司水印';
//调用api
$channelContent -> do1();
}
}
/*
底层类:微信的发布api
*/
class Wx{
function do1(){
echo '微信处理';
}
}
这个例子非常好.高层是发布,底层是api.业务功能依赖底层封装的sdk之类的基础模块.
第一:高层依赖底层的具体实现,并不是依赖一个抽象.因为对于Publish::doSomething(Wx $channelContent)明确要求要传递一个实现类的对象过来
第二:
(1)抽象依赖具体实现,
对于APublish::doSomething(Wx $channelContent)的实现依赖一个具体的实现(细节)
(2)具体实现没有依赖抽象,也依赖了具体实现
Wx类的实例,具体实现Publish::doSomething(Wx $channelContent)也就依赖了另一个具体实现,没有依赖抽象
(其实这是必然的,抽象类A依赖的是具体实现(细节)B,具体实现A1不也得依赖具体实现(细节)B).
改一下
<?php /* 抽象一个业务渠道出来 */ interface IChannelContent{ function deal(); } /* 还是那个发布类,原来是依赖具体实现现在依赖一个抽象的业务渠道概念 */ Abstract class APublish{ abstract function doSomething(IChannelContent $content); } /* 实现类也得改 */ class Publish{ function doSomething(IChannelContent $channelContent){ //... 发布相关的功能 echo '统一加上公司水印'; //调用api $channelContent -> deal(); } } /* 底层类:微信的发布api,这个是不需要变化的,可能你也变不了是一个公共模块 */ class Wx{ function do1(){ echo '微信处理'; } } /* * 一个业务系统根据业务接口封装出来的使用类,区分开底层sdk. * 注意:这时候理解Wx是一个很难变化的实现类,基本不会变化.类似固定的数据类型了(int,string,array这种) * */ class WxChannel implements IChannelContent{ protected $api; function __construct(Wx $wx) { $this->api = $wx; } function deal(){ $this->api->do1(); } }
但是有什么用呢?
任何抽象都能应对那个抽象目标的变化.
其实第一种写法可以应对doSomethinig处理方式的改变,比如统一处理添加了一个新能力 " 加上发布时间"这种,也不是不能用.
但是你从设计角度来说.在核心业务中调用基础功能,应该维护一个基础功能的抽象,称之为防腐层.才能有效的面对不可控的变化(别人写的你不能改).这是一行还好.如果散到代码里面到处都是,要是改就很闹心了.(给你个需求,记录一下调用的log!)
改完以后其实添加了另一个好处,突然发现渠道可以增加了
class Weibo{ function deal1(){ echo '微博处理'; } }
class Weibo implements IChannelContent{ protected $api; function __construct(Weibo $weibo) { $this->api = $weibo; } function deal(){ $this->api->deal1(); } }
对于认为Wx是基础类型的问题,如果有个interface IWx,感觉最开始的写法也是可以接受的,并不违反依赖倒置原则,但是IWx是个宽接口,而且不是防腐层抽象.也不推荐这么写
<?php interface IWx{ function do1(); function do2(); function do3(); //...好多好多api } class Wx implements IWx{ function do1(){ echo '微信处理1'; } function do2(){ echo '微信处理2'; } function do3(){ echo '微信处理3'; } //... 还得有实现 } abstract class APublish{ abstract function doSomething(IWx $wx); } class Publish{ function doSomething(IWx $wx){ //... 发布相关的功能 echo '统一加上公司水印'; //调用api $wx ->do1(); } }