对于一个核心类或者框架来说,通常会在运行时向外部回调多种操作以便于使用方做出一些主流程上面的AOP或者Intercept操作。而很多操作可能是预留不足以提供的,当我们逐渐去拓展我们的Callback时,使用方的代码将会大量增加,于是引入CallbackProxy的模式解决这个问题。
Callback问题
背景
1 | public class Manager { |
这是一个简单的Manager类,模拟的是一个底层框架向上层提供的管理类,这里先假设我们这个类的使用方有10个(在这里我们就编号C1~C10),而他们最终的运行环境都是一致的,只是开发期的环境不一致。我想过直接把一些真实场景中的代码拿出来举例,但是偏向业务的代码往往比较复杂且冗余,所以这里我们就简单粗暴地写了几个空方法模拟。其中operate()方法是提供给外部调用的方法,operate()方法中会调用到内部的a~e的一系列方法。
需求叠加
本来是一个简单的业务逻辑,但是突然有个业务方C1说他们需要在Manager的operate()方法的a()方法中添加一个回调,给业务方去处理。于是便有了我们的Callback接口:
1 | public interface Callback { |
并且我们还要改造我们的Manager类:
1 | public class Manager { |
我们在Manger类中添加一个Callback变量,并向业务方提供了setCakkback(CallbackA callback)方法为了方便外部把自己想在a()方法中做的操作set进来,这样处理既能让我们的业务方C1能够在a()方法中做他们定制化的处理,也不会影响到其他业务方的正常使用。
下面便是业务方C1的代码实现部分:
1 | public class ClientA { |
需求再叠加
由于业务是多变的,业务会随着许多主观以及客观的情况不断发生改变,某一天我们的业务方C2突然跟我们说,他们需要在b()方法中添加一个回调以处理他们内部的业务逻辑。于是我们开始考虑,上次C1提到要在a()方法中添加回调的时候,我们有创建了一个Callback的接口,并且有在Manager类中提供了setCallback()方法,那我们能不能为了代码的复用性而直接在Callback接口中添加一个回调方法callB()呢。但答案是否认的,因为如果你这样做了,业务方C1会莫名躺枪的……
既然我们不能添加Callback中的方法,又不想想我们对业务方C2支持的同时又去影响到C1,那我们只能新建一个接口CallbackB了:
1 | public interface CallbackB { |
于是,我们的Manager又得这样改变:
1 | public class Manager { |
我们开始看一下我们对Manager类的改动,我们这次的代码增量是:
1 | //1. 新增的CallbackB属性 |
我这里将增量部分切分为1、2、3三个部分。我们来仔细分析一下这三个部分,其中1和2其实是一种“无脑操作”,就是新增加一个回调接口,并提供回调接口的set方法而已,更甚至来说,3中的非空判断也都是冗余的。
仅仅为了满足C2的回调处理其实是简单的,只是既然今天C2会过来说是需要b()方法的回调,那接下来谁也不敢保证C3、C4、C5会不会再过来提出要在c()方法,d()方法,e()方法中添加对应的回调,甚至C3要abc的回调,C4要ace的回调。业务简单的时候,代码不一定是简单的,但是业务复杂的时候,代码一定是复杂的。
而且,站在职责单一的角度来看,对于我们的Manager类是不应该去维护这样一系列繁琐的Callback,并且还要在每个回调时机时进行非空判断的。
CallbackProxy模式
CallbackProxy的引入
这里我们引入CallbackProxy模式,我们先定义一个空接口:
1 | public interface Callback { |
这个接口就是对于Manager类的一个统一协定,协定所有Manager向外的回调都必须继承该接口。
接下来便有我们的一系列Callback:
1 | public interface CallbackA { |
1 | public interface CallbackB { |
1 | public interface CallbackC { |
然后有一个类来实现所有的Manager需要向外回调的Callback,然后都是一些空实现。
1 | public class SimpleCallback implements CallbackA, CallbackB, CallbackC { |
然后便有我们的重头戏CallbackProxy,它继承于SimpleCallback,然后内部存储一个真实的Callback,并向外提供一个setCallback()方法:
1 | public class CallbackProxy extends SimpleCallback { |
接下来我们来看一下我们的引入CallbackProxy之后的Manager类:
1 | public class Manager { |
我们可以看到,现在的Manager类不需要再声明一系列的Callback,再提供一系列的setCallback方法,再来一堆的非空判断,这些都让我们的CallbackProxy来做了。而我们的Manger类只需要专注于他自己的业务逻辑处理和选择合适的时机进行回调即可。
拓展性
上面的引入Callback的例子中我们写了a~c的回调,现在假如某个业务方还需要我们提供d()方法的回调,接下来我们只需要在新建一个CallbackD类,并在SimpleCallback和CallbackProxy做出对应的改变,然后在我们的真正做回调处理的Manager类中直接在d()方法中调用mCallback的callD()方法即可,代码如下:
1 | public interface CallbackD { |
1 | public class SimpleCallback implements CallbackA, CallbackB, CallbackC, CallbackD { |
1 | public class CallbackProxy extends SimpleCallback { |
1 | public class Manager { |
我们会发现我们的逻辑变得很清晰,类虽然多点,但是各司其职、有条不紊。
更高效开发
其实到了上面,CallbackProxy的介绍已经是完成了,但有一点需要说明,就是关于对Callback拓展的时候。我们发现其实我们真正做的回调操作仅仅是定义CallbackD和在Manager的d()方法中添加callB()的回调这两个地方。其他的在SimpleCallback和CallbackProxy的处理都是绝对无脑的,于是我们应用APT技术来简化我们的SimpleCallback和CallbackkProxy的处理(由于该篇不是为了介绍APT的,这里只把简单的使用效果贴出来)。
1 | ( |
这里我们只需要写一个接口继承我们所有需要添加的Callback,然后加上一些注解配置,即可自动生成对应的SimpleCallback和CallbackProxy类:
1 | public abstract class SimpleApi implements Api { |
1 | public final class ApiProxy implements Api { |
也就是说当我们需要拓展一个新的Callback时,只需要让Api这个接口多继承一个新的Callback并generate,然后
就可以在Manager类中添加对应的回调就ok了。