前言:个人认为:
接口是 反转A和B的依赖关系,让A依赖B反转为B依赖A。
事件是 反转A和BCDEF...的依赖关系,让A依赖BCDEF...,反转为 BCDEF... 依赖A。
原文链接:http://www.fancyecommerce.com/2016/07/02/对接口和事件概念的深究/
1.对于接口,个人认为更多的解决依赖颠倒。
接口的作用,1:可以解耦,我个人认为更确切的是,接口可以让依赖关系颠倒,原来是a和b的关系(a依赖b),变成了a和c(a定义c),b和c的关系(b依赖c),其中c是接口,通过中间层c,间接让b依赖a,有点控制反转的味道。 2.接口,定义类的某些方法不能更改方法名和方法传入的参数。
工作顺序的颠倒: 两个人合作做一个功能,A和B分别做2个模块,A是消费方,B是生产方,原来的先后顺序为: B生产出来一个方法,告诉A调用这个方法,来消费B生产的方法,
变成了:A定义个接口C,然后A使用C接口里面的方法实现了自己的功能,然后把C接口扔给B,让B去实现这个接口。
由生产方做主导的工作顺序,转变成消费方做主导的工作顺序。
详细的请看:http://www.fancyecommerce.com/2016/07/01/对接口的见解-yii2/
2.关于事件:
事件给人感觉很啰嗦,下面举个例子: 在电商系统加入购物车功能中,定义 A .为加入购物车,把产品的信息写入到购物车表中。 A 完成了后,老板说,加入购物车功能中得加入一个log记录功能,然后程序猿在原来的文件中加入了 B 日志功能 后来,老板说,加入购物车扣库存,程序猿在原来的文件中加入了功能C,扣产品库存 后来,老板说,加入优惠券,程序猿在原来的文件中加入了D功能 a,B,C,D功能都在一个文件中,后期很难维护。
升级模式:
分为购物车模块A,log模块B,产品模块C,优惠券模块D 让BCD三个模块实现对应的方法,然后给予A,然后A 实现购物车模块的加入购物车
也就是说,A需要等BDC三个模块完成,然后A才能完成,也就是说A依赖于BCD
继续升级:
A做了购物车模块,然后抛出事件,然后执行事件,后续如果有BCD想要添加功能,直接在事件触发前绑定事件就可以 这样,这样通过事件的配置文件,实现了解耦,也就是A的功能不依赖于BCD来,A 只需要抛出事件,然后BCD绑定过去,然后A触发即可, 这样A不依赖于BCD了。A可以先完成工作,然后,BCD的事件函数做好后,绑定上就可以, 而且在使用购物车模块的时候,我有很多事件,如果我想使用B日志功能,我就绑定上去,如果不使用我就不绑定, 这样就可以在不同的场景下面,有不同的购物车功能。
在上面,我们可以看到,每次添加事件,对于A来说,还是要更该A里面的内容,绑定事件。 我们进行下面的思考:
我是一个电商的开发商,你是一个我的系统的使用者,还是老样子啊,我的库包你是不能改动的 但是你又想改动我的功能 我在我的库包中,做了一个加入购物车的功能,请问,你如何做到不改动我的文件的前提下,在加入购物车这个动作中,添加你的一个功能呢,譬如你想在加入购物车的时候,添加一个日志记录功能。
解决办法:还是依赖注入,依赖的是配置文件,在配置文件中加入事件,我在做加入购物车的功能的时候,就想到了肯定很多人想在这里加代码,哪里我就提前部署好了事件, 通过配置文件中的事件配置,来绑定事件执行。 如果你想在加入购物车这个动作中加入你的代码,你可以做一个事件,然后在配置中加入你写的事件配置就可以了,对吧? 你没有改动我的系统文件,你改动的是配置文件。
总结:
接口:是为了解决依赖关系,由A依赖B,变成了,A定义接口,让B依赖接口,间接实现B依赖A
事件:A依赖BCD,变成了A定义事件,让BCD依赖事件,间接实现了让BCD依赖A,
配置文件注入:通过配置文件注入到组件的初始化中,可以在配置文件中添加事件配置,间接实现了 更改系统功能,但是不改动系统文件内容。
本文由 fecommerce 创作,采用 知识共享署名 3.0 中国大陆许可协议 进行许可。 可自由转载、引用,但需署名作者且注明文章出处。
微信
@Kittyfamous #1楼 容器是依赖注入,一个对象的初始化,依赖于注入的配置,容器是面向的单个对象的初始化。
而接口是两个模块间通信达成的协议。在执行方面可以颠倒依赖关系,
事件是多个模块之间通信的方式。当然事件为了更好的使用,也会使用容器注入配置,进而通过配置添加更多的事件,而不是更改里面的代码,这样,通过配置文件,完成了解耦,或者说松耦合。
有点问题…… 反转A和B的依赖关系,让A依赖B反转为B依赖A
,那 B 依赖于 A 又怎么解决……
看这个 https://codingstyle.cn/topics/192:
依赖倒置原则则强调:为了让依赖关系是稳定的,不应该由实现侧根据自己的技术实现方式定义接口,然后强迫上层(即客户)依赖这种不稳定的API定义,而是应该站在上层(即客户)的角度去定义API(正所谓依赖倒置)。
但是,虽然接口由上层定义,但最终接口的实现却依然由下层完成,因此依赖倒置描述为:上层不依赖下层,下层也不依赖上层,双方共同依赖于抽象。
还有这里 http://my.oschina.net/zgldh/blog/389243#OSC_h1_1 ,Laravel 框架的作者写的:
最后的原则是依赖反转原则,它规定高等级的代码不应该依赖低等级的代码。首先,高等级的代码应该依赖着抽象层,抽象层就像是“中间人”一样,负责连接着高等级和低等级的代码。其次,抽象定义不应该依赖着具体实现,但具体实现应该依赖着抽象定义。
@fecommerce [[#5楼](#comment5)](#comment5)
我只是不赞成 间接实现B依赖A
这个说法哈,因为 B 只依赖于接口,实例化 B 的时候和 A 没有任何关系。
class A
{
public function foo(){
$b = new B();
$b->bar();
}
}
class B
{
public function bar(){
}
}
这是 A 依赖于 B 的情况。
interface IB
{
public function bar();
}
class B implements IB
{
public function bar() {}
}
class A
{
private $b;
public function __construct(IB $b)
{
$this->b = $b;
}
public function foo(){
$this->b->bar();
}
}
定义一个接口 IB
,然后通过构造方法把依赖注入进去。
就变成了的 A 依赖于接口 IB
,在实例化 A 的时候,需要传入接口 IB
的实现,这里可以说 A 间接依赖于 B
。
$a = new A(new B());
但是 B 只依赖于接口 IB
,是接口的一个实现,和 A 没有关系,并没有 间接实现B依赖A
。
$b = new B();
举这么个例子吧。 你定义了一个类,你需要做一个搜索功能,但是你不会写,需要你的同事来完成, 因此,你需要等你同事写完类,然后告诉你各个函数具体做什么的,然后你来调用。然后你在写你后面的逻辑。
你可以以另外一种更加高效的方式,不需要你同事写完搜索功能,你直接定义一个接口,扔给你的同事,让他给你实现这个搜索接口即可。而且这种方式,蛮高效的,你可以先通过定义接口,把整个功能的结构架子给搭起来。