回调函数的基础概念:
回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。(百度百科)
在最开始接触到"回调函数"这个概念的时候,自己一直都没能理解"回调函数"的优点。感觉自己写的方法很少用这种概念,可是又不理解为什么php或者js当中,很多内置函数都用了回调的思想。下面将以一个通俗的事例,来为初学者剖析一下"回调函数"的使用场景,如果有写的不正确的地方,还希望大家指正。
在某个公司当中,A是技术总监,手下管理了以群小弟(B、C、D、F....);
突然有一天,公司下达了命令,要做一个商城类系统;这个商城系统的商品主要分为两大体系,虚拟物品与实物物品;
A 总领全局,商城两大体系商品相关的功能拆分给了B(负责虚拟类商品)和C(负责实物类商品)两个助理来负责;
架构体系一:
A负责了支付核心部分的代码架构,封装了一个方法,主要解决所有商品类别的支付订单汇总、调用银行接口转账汇款;
B和C负责各自商品订单的逻辑处理,到支付环节时统一调用A封装的方法(在这个过程当中,可能会涉及到A设计的数据表与B、C数据表中数据相互写入的情况);
思路确定后,大家开始干活儿。
- 处理方式一:
A告诉大家自己方法返回数据的格式,B和C直接调用A的方法,然后通过A返回的数据,继续操作各自的订单;
coderA:
function A(){
//1.处理支付
code_pay
//2.公用订单
code_common_order
}
coderB:
function B(){
//支付订单
A()
//支付成功后,虚拟类商品订单的处理
code_B
}
coderC:
function C(){
//支付订单
A()
//支付成功后,实物类商品订单的处理
code_C
}
A看了B、C写的代码过后,建议B、C把支付成功部分的代码也封装起来;
- 处理方式二:
coderA:
function A(){
//1.处理支付
code_pay
//2.公用订单
code_common_order
}
coderB:
function B(){
//支付订单
A()
//对虚拟类商品订单处理封装
b_order()
}
function b_order(){
//支付成功后,虚拟类商品订单的处理
code_B
}
coderC:
function C(){
//支付订单
A()
//对实物类商品订单处理封装
c_order()
}
function c_order(){
//支付成功后,实物类商品订单的处理
code_C
}
处理方式二相对于处理方式一而言,代码的模块化程度更高,可读性也会更强。
在架构体系一的前提下,我们引入回调的思想。
A告诉B和C,调用他方法的同时,干脆B、C也把处理各自类别订单的方法通过参数的形式传给自己,然后在A写的方法中执行处理不同类别商品订单的数据状态。
- 处理方式三:
coderA:
function A(Callable $HandleOther){
//1.处理支付
// code_pay
//2.公用订单
// code_common_order
//3.处理各类订单
$HandleOther();
}
coderB:
function B(){
A( 'b_order');
}
function b_order(){
//支付成功后,虚拟类商品订单的处理
code_B
};
coderC:
function C(){
A( 'c_order');
}
function c_order(){
//支付成功后,实物类商品订单的处理
code_C
}
在第三种方式当中,实际上就是采用了回调的思想。但是基于架构体系一的前提下,笔者认为这种方式与处理方式二的效果是差不多的;并且处理方式三因为采用了回调,在代码的可读性上相对于处理方式二并没有提升,甚至还有下降。
那回调应该用在什么地方比较合适呢?
架构体系二:
A负责了支付核心部分的代码架构,封装了一个方法,这个方法不仅仅要解决所有商品类别的支付订单汇总、调用银行接口转账汇款,而且A还希望在这个方法中,同时就将各类订单支付后需要处理的逻辑给一同处理了;
B和C同样也是主要负责各自商品订单的处理;
基于这种架构,引出以下几种处理方式
- 处理方式四:
function HandleOne(){
//1.处理支付 coderA
// code_pay
//2.公用订单 coderA
// code_common_order
//3.如果是虚拟类订单,支付成功后,虚拟类商品订单的处理 coderB
// code_B
//4.如果是实物类订单,支付成功后,实物类商品订单的处理 coderC
// code_C
}
处理方式四,代码耦合高,coder之间层次不明确。
A大佬一使用就觉得不科学,要求B、C分别将自己的代码封装起来,自己主动调用;
- 处理方式五:
coderA:
function HandleTwo(){
//1.处理支付
code_pay
//2.公用订单
code_common_order
//3.如果是虚拟类订单,支付成功后,虚拟类商品订单的处理
CodeB()
//4.如果是实物类订单,支付成功后,实物类商品订单的处理
CodeC()
}
coderB:
function CodeB(){
//支付成功后,虚拟类商品订单的处理
code_B
}
coderC:
function CodeC(){
//支付成功后,实物类商品订单的处理
code_C
}
处理方式五相对于处理方式四而言,耦合度有所降低,并且各个coder之间的代码可以分层架构,缺陷在于如果每产生一种新的商品类型订单,A都需要来更新自己的代码。
其实到这里大家都应该理解到回调的好处了。
- 处理方式六:
coderA:
function HandleThree(Callable $HandleOther){
//1.处理支付
code_pay
//2.公用订单
code_common_order
//3.无论是什么订单,我们都执行 $HandleOther 的这个方法
$HandleOther();
}
coderB:
function executeB(){
//处理一些其他逻辑
xxx_code
//处理支付逻辑
HandleThree('CodeB');
}
function CodeB(){
//支付成功后,虚拟类商品订单的处理
code_B
}
coderC:
function executeC(){
//处理一些其他逻辑
xxx_code
//处理支付逻辑
HandleThree('CodeC');
}
function CodeC(){
//支付成功后,实物类商品订单的处理
code_C
}
处理方式六就是典型的一种采用了回调来解决问题的思路。降低了耦合,提高了层次划分(A写底层函数时,只需要告诉调用者回调函数相关的参数)、各个层次代码之间影响降低(新增多少商品类型订单,A都不需要对自己方法进行改动);
当然,处理方式六所体现出来的好处,主要是基于架构体系二(A不仅仅只希望处理支付相关流程,也希望完成对支付后各类订单状态的同步);如果采用的是架构体系一,回调的意义其实并不大。
其实回调函数本身是一种破坏系统结构的设计,一般而言回调函数都会改变系统的运行轨迹,执行顺序和调用顺序;
但是在某些设计思路作为前提下,采用回调的方式可以大大提高我们代码的可读性以及降低代码耦合度。
笔者写此文的目的就是为了告诉大家,用回调并不意味着一定就是好的。如果各位coder的回调仅仅是让配合开发的工作人员感到晕头转向,那还是采用常规一点的写法来解决问题,不要再折磨大家了。
但是如果有一天,当配合开发的工作人员看到你的回调后,猛拍大腿,wocao~,代码居然还能这样写!给人一种焕然一新、恍然大悟的感觉之时,那就说明各位已经掌握了回调的精髓了。
以上仅仅是笔者对回调的理解,若有错误,欢迎指正。
原创不易,转载请指明出处!