模板方法(下)
回调也可以实现类似于模板模式的作用。
回调的原理解析
相对于普通的函数调用来说,回调是一种双向调用关系。A 类事先注册某个函数 F 到 B 类,A 类在调用 B 类的 P 函数的时候,B 类反过来调用 A 类注册给它的 F 函数。这里的 F 函数就是“回调函数”。A 调用 B,B 反过来又调用 A,这种调用机制就叫作“回调”。
A 类如何将回调函数传递给 B 类呢?不同的编程语言,有不同的实现方法。C 语言可以使 用函数指针,Java 则需要使用包裹了回调函数的类对象,我们简称为回调对象。
1 | public interface ICallback { |
从代码实现中,我们可以看出,回调跟模板模 式一样,也具有复用和扩展的功能。除了回调函数之外,BClass 类的 process() 函数中的 逻辑都可以复用。
如果 ICallback、BClass 类是框架代码,AClass 是使用框架的客户端代码,我们可以通过 ICallback 定制 process() 函数,也就是说,框架因此具有了扩展的能力。
我们使用了框架的代码逻辑,而且利用回调,在框架的逻辑中插入了我们需要做的修改,因此说具有了扩展能力。
Spring 提供了很多 Template 类,比如,JdbcTemplate、RedisTemplate、 RestTemplate。尽管都叫作 xxxTemplate,但它们并非基于模板模式来实现的,而是基于回调来实现的,确切地说应该是同步回调。
模板模式 VS 回调
从应用场景上来看,同步回调跟模板模式几乎一致。它们都是在一个大的算法骨架中,自 由替换其中的某个步骤,起到代码复用和扩展的目的。而异步回调跟模板模式有较大差别, 更像是观察者模式。
从代码实现上来看,回调和模板模式完全不同。回调基于组合关系来实现,把一个对象传递 给另一个对象,是一种对象之间的关系;模板模式基于继承关系来实现,子类重写父类的抽 象方法,是一种类之间的关系。
在代码实现上,回调相对于模板 模式会更加灵活,主要体现在下面几点:
- 像 Java 这种只支持单继承的语言,基于模板模式编写的子类,已经继承了一个父类,不 再具有继承的能力。
- 回调可以使用匿名类来创建回调对象,可以不用事先定义类;而模板模式针对不同的实 现都要定义不同的子类。
- 如果某个类中定义了多个模板方法,每个方法都有对应的抽象方法,那即便我们只用到 其中的一个模板方法,子类也必须实现所有的抽象方法。而回调就更加灵活,我们只需 要往用到的模板方法中注入回调对象即可。
参考
《设计模式之美》