对接口和事件概念的深究 -设计模式的依赖颠倒

技巧库 · fecommerce · 于 6年前 发布 · 5404 次阅读

前言:个人认为:

接口是 反转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 中国大陆许可协议 进行许可。 可自由转载、引用,但需署名作者且注明文章出处。


微信

本帖已被设为精华帖!
共收到 9 条回复
Kittyfamous#16年前 0 个赞

有容器,这些还有价值不?

fecommerce#26年前 1 个赞

@Kittyfamous #1楼 容器是依赖注入,一个对象的初始化,依赖于注入的配置,容器是面向的单个对象的初始化。

而接口是两个模块间通信达成的协议。在执行方面可以颠倒依赖关系,

事件是多个模块之间通信的方式。当然事件为了更好的使用,也会使用容器注入配置,进而通过配置添加更多的事件,而不是更改里面的代码,这样,通过配置文件,完成了解耦,或者说松耦合。

3楼 已删除.
ilvsx#46年前 0 个赞

有点问题…… 反转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#56年前 0 个赞

@ilvsx #4楼 由A依赖B,变成了,A定义接口,让B依赖接口,间接实现B依赖A

fecommerce#66年前 0 个赞

在A中,对B的调用,通过 instance of 判断一下B是否实现了接口,如果没有实现就报错。

ilvsx#76年前 0 个赞

@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();
fecommerce#86年前 0 个赞

@ilvsx #7楼 你在 class A的构造方法里面要添加类型判断,用instance of ,如果Class B没有实现接口 IB, 那么在构造方法中应该要报错的。

结果就是:A定义接口IB , 也就是说,这个接口的存在,是一个条约,是一个规范,是一个协议,这个协议是由A来决定的,实现了这个协议的对象,才可以作为A的一部分,譬如:A是自行车,B是轮子,那么A定义了一个接口,来定义传入的轮子的规范,那么b如果符合这个规范,b就可以作为A的一部分,如果B不符合这个规范,譬如B是汽车轮子的尺寸,强行安装到A上面,就会报错。

如果A不存在,那么接口也就没有了意义。

这是一个思想理念吧。

fecommerce#96年前 0 个赞

举这么个例子吧。 你定义了一个类,你需要做一个搜索功能,但是你不会写,需要你的同事来完成, 因此,你需要等你同事写完类,然后告诉你各个函数具体做什么的,然后你来调用。然后你在写你后面的逻辑。

你可以以另外一种更加高效的方式,不需要你同事写完搜索功能,你直接定义一个接口,扔给你的同事,让他给你实现这个搜索接口即可。而且这种方式,蛮高效的,你可以先通过定义接口,把整个功能的结构架子给搭起来。

fecommerce#106年前 0 个赞

@ilvsx #7楼 文字上面没有必要那么抠字眼,理解意思,并在你的程序中应用就好,对于业务驱动的公司产品线来说,很少会用接口,基本都是一个人搞一块,乱糟糟的,虽然是面向对象编程,里面的逻辑基本都是面向过程,有的时候一个函数,几百行, 很少使用类变量,都是参数在各个类函数中传来传去。

添加回复 (需要登录)
需要 登录 后方可回复, 如果你还没有账号请点击这里 注册