最有用的设计模式之一-模板方法

今天是设计模式学习系列的第9篇,为大家带来一个使用特别广的设计模式 – 模板方法模式

从问题出发

  1. 什么是模板方法模式?
  2. 常说的钩子方法是什么?和模板方法的关联?
  3. 模板方法模式和策略模式好像有点像,如何区分?
  4. JDK 中的具体体现?

模式解析

一如既往的为了方便理解,我们从一个日常开发中的例子出发。
做保险系统的都知道,保险分为很多种类,比如:产险、寿险、养老险、健康险、车险等。 我们作为后台开发者在设计开发这些保险产品上线的时候,首先要分析下这几个险种购买流程的共同点?

这些保险在产品购买时的流程如下(举个例子,实际情况有所差异,核心流程属于公司机密,必须严格保密,开个玩笑啦!):

健康险、产险、养老险、寿险、车险:

  1. 保费试算
  2. 健康告知(车险没有,是检验车牌)
  3. 核保
  4. 支付(统一的)
  5. 承保

可以看到,核心的购买流程是非常相似的,但是不同的险种具体实现是不一样的,比如健康险和产险他们健康告知的内容就是不一样的。

如果直接搞定几个对应的类实现,肯定会存在很多重复的代码,如果让你来设计,肯定第一个想法就是定义一个超类,将共同的方法放在其中,然后购买 createOrder() 方法由于在子类(车险和产寿养健)中的流程调用方法是不一样的,所以定义成抽象,然后例子中的健康告知等方法是不公用的所以放到具体的子类中。

但是这样的设计,还是可以优化的,因为投保下单的流程各个险种基本上是一样的,至于每个险种可能在每个步骤上的实现不一样,我们可以将其抽象化。比如将健康告知和检验车牌统一抽象成 check() 方法。 这样一来新的 createOrder() 方法看起来就是这样:

void createOrder() {
    
    
    calculate();
    check();
    underwriting();
    pay();
    accept();
}

然后我们在超类中将公用的方法实现,然后 不一样的方法 我们定义为抽象,推迟到子类去实现,比如 check() 方法,就放到对应的 车险 和 寿险等子类中去各自实现,这样一来就简化了代码,实现了复用。

认识模板方法

基本上,我们刚刚举的例子就是模板方法模式。上述中的保险基类中的 createOrder() 就是我们的模板方法。 这是为什么?

因为:

  1. 毕竟它是一个方法;
  2. 它用作一个算法的模板,在这个例子中,算法是用来完成保险产险的投保流程的。

在这个模板中,算法内的每一步骤都被一个方法代表了,某些方法是有这个类(即超类)处理的,某些方法则是由子类处理的。需要由子类提供的方法,在超类中必须声明为抽象。

定义模板方法模式

模板方法模式 在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以在不改变算法结构的情况下,重新定义算法中的某些步骤。

这个模式可以确保算法的结构保持不变,同时由子类提供部分实现,我们来看下类图:

在这里插入图片描述

模板方法带给我们什么

  1. 由 AbstractClass 类主导一切,算法是不可改变的;
  2. 对于子类来说, AbstractClass 的存在,可以将代码的复用最大化;
  3. 算法只存在于一个地方,所以容易修改;
  4. 这个模板方法提供了一个框架,可以让其他子类插进来,比如增加其它险种,只要实现自己的方法就可以了;
  5. AbstractClass 类专注在算法本身,而由子类提供完整的实现;

什么是钩子方法

钩子 相信大家曾经有听到过,但是可能一直不太理解,它是一种被声明在抽象类中的方法,但只有空的或者默认的实现。 钩子的存在,可以让子类有能力对算法的不同点进行挂钩。要不要挂钩由子类自行决定。

它和模板方法是搭配着使用的,比如我们上面定义的投保算法 createOrder() 假设现在要加入一个新的 方法调用,改变了模板方法。但是这个方法又不是所有的子类都需要实现,希望将这个选择的能力交给子类选择,就可以将这个方法在超类中定义为空方法或者默认实现,子类可以选择覆盖,但不一定非要这么做,这就是钩子的作用。

模板方法和策略模式的区别?

策略模式是我们第一篇文章讲解的,可能有很多小伙伴已经不记得了,那么这里在重复下:策略模式定义了一个算法家族,并让这些算法可以互相转换。正因为每一个算法都被封装起来了,所以客户可以轻易地使用不同的算法。

虽然两者都封装了算法,但是意图是不一样的:模板方法的工作是定义一个算法大纲,而由子类定义其中某些步骤的内容,它可以改变个别步骤的实现细节但是算法的结构依然维持不变。不过策略模式是使用组合委托的方法,通过对象组合可以让客户选择算法的实现。

也就是说模板方法对算法的控制权更多,而且不会重复代码,一般情况下效率高一些,而策略模式更加灵活,更具有弹性。

身边的模板方法

  1. Java数组提供的排序就是一个模板方法,静态的Arrays.sort(Object[]),具体的实现大家可以去看JDK源码,简单的说就是 sort 是一个模板方法,然后需要我们自定义实现 compareTo() 方法,用来“填补”模板方法的缺陷;
  2. Java并发包的大核心,AbstactQueuedSynchronizer(简称AQS),源码也希望大家去看下,验证下今天学习的模板方法,简单描述就是 其提供的 acquire() 方法用来获取锁,但是需要具体的锁实现类去自定义完成 tryAcquire() 方法,已完善锁获取的流程,即用这个钩子和AQS的锁获取算法挂上钩,非常的经典;

围炉夜话

  • “模板方法”定义了算法的步骤,把这些步骤的实现延迟到了子类;
  • 模板方法模式为我们提供了一种代码复用的重要技巧;
  • 模板方法的抽象类可以定义具体方法、抽象方法和钩子;
  • 抽象方法由子类实现;
  • 钩子是一种方法,它在抽象类中不做事,或者只做默认的事情,子类可以选择要不要去覆盖它;
  • 为了防止子类改变模板方法中的算法,可以将模板方法定义为final;
  • 策略模式和模板方法模式都封装算法,一个用组合,一个用继承;
  • 工厂方法是模板方法的一种特殊版本,原语意图是用来创建并返回对象;

希望大家能有收获!

个人公众号

在这里插入图片描述

  • 觉得写得还不错的小伙伴麻烦动手点赞+关注
  • 文章如果存在不正确的地方,麻烦指出,非常感谢您的阅读;
  • 推荐大家关注我的公众号,会为你定期推送原创干货文章,拉你进优质学习社群;
  • github地址:github.com/coderluojust/qige_blogs

猜你喜欢

转载自blog.csdn.net/taurus_7c/article/details/107584205