文章目录
- 代理
- 委托
- 委托与代理的区别
- 回调
- 回调函数
- 回调函数与普通函数的区别
- 钩子
- 广义的钩子
- 钩子与代理的区别
- 钩子与委托的区别
- 钩子与回调函数的区别
- 句柄
- 句柄与钩子的区别
- 打桩
- 打桩与代理的区别
代理
代理(proxy):被代理类写好一套 API 的实现对外开放使用,代理类在被代理类的 API 的基础上再封装一层,形成一套与被代理类接口不变的新 API 对外开放使用。
委托
委托(delegate):委托类事先写好一段程序流程,但某个主要环节的具体实现不由自己决定,而是委托给其它类来实现。委托类会当作这个实现已经写好,直接调用。
委托与代理的区别
某种程度上来说,代理类有点像委托类,它们都只负责调用,但不负责编写被调用代码具体的实现。最大不同之处在于它们出现的时间顺序正好相反,而且依赖关系不同。
在代理模式中,被代理类先出现,代理类后出现。被代理类在设计的时候不考虑自已以后会不会被代理。代理类低耦合依赖被代理类,但被代理类自身完全不需要代理类,它不在乎以后是否被代理。
在委托模式中,委托类先出现,被委托类后出现。委托类知道被委托类以后会出现,但却只负责调用,不负责编写“写死”的代码。委托类与被委托类是一种低耦合、互相依赖的关系,它们双方不能缺少另外一方。
一般来说,一个活动分为环境、定义、实现、使用。代理类、委托类的设计都是为了遵守设计模式中的单一职责原则,将一个活动的使用与实现分离。可代理类把一个活动的定义与实现耦合在了一起,对外的环境是透明的,因此对内高内聚、对外低耦合。但委托类却把一个活动的环境、定义与使用耦合在了一起,因此局限性较大。
因此,委托模式的耦合度高于代理模式,因此使用场景较少。通常,如果程序没有高内聚的要求,会使用更低耦合的钩子来代替委托。
另外,对于某个局部的步骤,代理类会在被代理类基础上增加一些代码,而委托类基本不做任何多余的事情,完全放权给被委托类完成。委托类更多地只是向被委托类提供一种运行环境。
回调
回调(callback)指的是某函数在执行的时候,由于某些原因,该函数被迫中止执行,交出当前线程的使用权,当前线程转而执行其它函数的过程。因为一般等这个函数执行完毕时,当前线程的使用权会返回到之前中止执行的那个函数中,所以将这个过程称为回调。
回调中的“调”,指的是那个被迫中止执行的函数对“其它函数”的调用。回调中的“回”,指的是那个“其它函数”以后将会将当前线程使用权返回给那个被迫中止执行的函数。这个“其它函数”往往不是自己编写的函数,而是源自于外源注入。
回调函数
回调函数指的即为上述“回调”中的“其它函数”。回调函数一般是经过事先设定(传入),等到某事件发生时就被触发的函数。比方说,事件监听器中设定的函数等等。
有些时候,如果回调函数执行时间很短,有返回值且能控制程序的走向,那么也把它称之为 钩子
(hook)。
在有些编程语言中,如果允许回调函数使用传入处的局部变量,那么也把它称之为 闭包
(closure)。
回调函数与普通函数的区别
回调函数与普通函数的区别在于,一般的函数是自己负责编写实现,自己负责调用。而回调函数是自己负责编写实现,但自己不负责调用。自己只是把函数具体实现编写好,然后交给别人来调用。
另一个区别是,(为了方便,这里将调用回调函数或普通函数的函数称之为主函数)。主函数在调用普通函数时,数据的传输流程一般为,先从普通函数调用时的实参传入,然后到达普通函数的形参,最后由普通函数的返回值到达主函数。对不同的编程语言,有些时候,普通函数也可以通过传入的实参、抛出的异常来将结果反馈给主函数。
但主函数使用回调函数时,这个过程描述起来就比较复杂了。回调函数通常是异步调用的,因为在主函数传入回调函数时,并没有马上执行该函数,而是改为异步执行,因此回调函数向主函数反馈结果时也一般不能像普通函数一样通过返回值来进行同步阻塞式反馈。通常,这同时还使用了一种称之为 闭包
的技术,来使得回调函数在真正执行时,可以修改主函数的数据,来达到来主函数反馈数据的目的。
【附】
-
什么是闭包:https://blog.csdn.net/wangpaiblog/article/details/113360870
-
什么是同步与异步、并行与并发、阻塞与挂起:https://blog.csdn.net/wangpaiblog/article/details/116114098
-
什么是同步阻塞、同步非阻塞、异步阻塞、异步非阻塞:https://blog.csdn.net/wangpaiblog/article/details/117236684
钩子
钩子(hook)指的是一个程序预设的子程序跳转入口。在不同的编程语言、不同场景下,钩子可以指预先约定的某一类函数名、某个类的接口引用字段等等。
钩子在生活中指的是可以钩住其它东西的一种工具,只要这个东西上有能被钩子钩住的圆环即可。如果一个工具上带有钩子,我们就可以在不破坏这个工具的前提下用我们自己的工具连接这个工具,这只需要在我们自己的工具上安装一个圆环即可。在编程中,这里的钩子相当于一个第三方的黑盒,圆环相当于一个对方指定的要实现的接口,只要自己编写的程序遵守了对方的规范并实现了这个接口,就可以在不需要知道和改动黑盒内部结构的情况下让该黑盒使用自己。
钩子是 IoC
思想的体现。它们有时候也称之为一种程序里的后门。它们体积不大,运行时间不长,但却能控制程序的走向。它是开发者提供给用户的一个功能强大的工具。
在有些场景中,钩子是一种回调函数(callback)。
广义的钩子
广义的钩子指的是一个可以控制程序流程的一段代码。但这段代码与程序其它部分的代码属于低耦合,即可以通过不改动其它部分的代码的条件下,任意地更改这段代码来控制程序的走向。
因此,广义的钩子不仅包括上面介绍的钩子,还包括一些类中预设的布尔函数,比方说,可以对某一系列的类中都设置一个布尔函数,然后用该布尔函数的返回值来决定程序走向,则该布尔函数就是一个广义的钩子。
钩子与代理的区别
钩子和代理也有些渊源。在某种角度上,含钩子的类也可以看成是一个代理类。但钩子和代理的区别在于,它们设计思路(流程)正好相反。对于代理,一般来说,被代理的类是很早以前就已经设计好了,而代理类依赖于被代理的类且设计时间晚于被代理类。但对于钩子,含钩子的程序往往是提交已经设计好了,之后只需要在使用该程序之前,将“圆环程序”提供该“钩子程序”,就可以使用该“钩子程序”了。可以看出,虽然“钩子程序”是依赖“圆环程序”的,但“圆环程序”的设计时间与“钩子程序”相对独立,是可以晚于“钩子程序”的。另外,“钩子”并未严格限定“圆环程序”的内容,“圆环程序”只需要遵守某种约定的规范,其内容可以自由发挥。这就是 IoC 思想。如果硬要把代理中的概念与钩子相对应,那么从这个角度上,代理与钩子的区别在于,对于代理,可以自由发挥的是代理类,而对于钩子,则是被代理类。
钩子与委托的区别
钩子可以看作是委托的升级版或者是特别版,其耦合度比委托要低。
在下面的这些情况下,应当设计成钩子而不是委托:
-
程序已经抽象出来一套生命周期
-
活动的实现与使用之间没有高内聚的要求
-
需要将接口对第三方暴露
-
对外暴露的接口的返回值将决定程序的走向
-
对外暴露的接口的实现不是一个耗时的操作
钩子与回调函数的区别
钩子与回调函数这两个词的意思非常接近,只是描述事物的侧重点有些不同,就像等边三角形与等角三角形,因此经常混用。
钩子相对于普通的回调函数,通常有如下特点:
-
内容短小、操作步骤很基本、耗时小
-
可以通过其返回值来控制程序走向
-
一般不会开启新线程或后台任务
回调函数相对于钩子,通常有如下特点:
-
回调函数由事件触发,且允许反复触发
-
对于 UI 领域,这种函数往往是
纯函数
,且返回值常常为空(void) -
当程序的运行轨迹改变时可以触发它,但它不会主动控制程序走向
关于什么是纯函数,可见笔者的另一篇博客:
什么是纯函数?:
https://blog.csdn.net/wangpaiblog/article/details/114005888
句柄
句柄(handle)指的是一种通过它能够访问程序内部,甚至控制程序的某种数值。在不同的编程语言、不同场景下,它可以指的是一种内存地址、指针、类对象等等。
【注意】
句柄的英文是 handle
,而不是 handler
。但 handler
也是一个编程中的术语,它和 handle
完全不是同一个东西。但由于英文语法与中文语法的区别,很难找到一个没有用过的两个字的中文词汇来翻译它。handler
实际上是一种事件处理程序(处理器),英文解释是 callback routine
,因此,它的意思更接近回调函数(callback)或者钩子(hook)。所以,handler
并不是句柄的意思。
句柄与钩子的区别
钩子是开发者提供给用户的一个功能强大的工具。就功能强大而言,这有点类似于句柄。但从使用方法来说,它们更像是相反的东西。
句柄提供了一些可供调用的 API,而钩子则要求自己实现这些 API 的内容,然后注入到程序中。
打桩
在编程中,桩(stub)是指用于模拟真实环境的代码段。在开发者测试中,常常需要模拟真实的环境来用于测试。因为构建真实的环境往往成本很大或者不可能,这就需要模拟。在开发者测试中,一个大型的项目必须要分解成各个基本的单元,先进行单元测试(UT),然后进行集成测试(IT),最后进行系统测试(ST)。分开测试的各个部分的碎片化的模拟环境称为桩。桩一般就具体为,对真实环境进行模拟所编写的函数。对桩函数的编写称为打桩。
打桩与代理的区别
代理和打桩都有一种伪装、等效替代的思想。桩函数相当于代理类,都喜欢对外伪装成使用者原来想要使用的东西。但区别在于打桩是测试领域使用的术语,而代理是开发领域使用的术语。