学习
实践
活动
专区
工具
TVP
写文章

设计模式篇——模板方法模式详解

需求:编码实现汽车功能

现在基本上每家都有汽车,我们也都知道汽车都有以下功能:启动、停止、鸣笛、跑。知道了这些,我们就可以用代码去完成这个功能。

先设计类图如下:

我们定义了一个抽象父类,里面定义了公有的方法,然后两个子类去实现这些方法。

抽象父类AbstractCar源码如下:

两个子类去实现自己的逻辑:

调用端:

执行后发现,两辆汽车都能正常运行。但仔细看应该已经发现问题了,Car1 和 Car2 的两个 run() 方法中的代码是一模一样的,代码冗余,我们需要将其提取到父类中。修改后的父类代码如下:

两个子类只需要删除重写的run()方法即可:

调用端的代码不需要改变,执行之后,发现也能正常运行。这就是传说中的模板方法模式。

模板方法模式的定义

模板方法模式定义了一个算法的步骤,并允许子类别为一个或多个步骤提供其实践方式。让子类别在不改变算法架构的情况下,重新定义算法中的某些步骤。

模板方法模式有一个通用的类图:

模板方法模式仅仅使用了Java的继承机制,其中AbstractClass叫做抽象模板,它的方法分为两类:

1. 基本方法,是由子类实现的方法,并且在模板方法被调用。

2. 模板方法,可以有一个或几个,一般是一个具体的方法,也就是一个框架,实现对基本方法的调度,完成固定的逻辑。

注:为了防止恶意的操作,一般模板方法上都加上final关键字,不允许被重写。

类图中的 ConcreteClass1 和 ConcreteClass2 属于具体模板,实现父类中定义的抽象方法。

我们来看下通用的源代码,抽象父类AbstractClass的代码如下:

子类的源代码如下:

调用方就很简单了,如下:

使用建议:抽象父类中的基本方法尽量设计为protected类型,复合迪米特法则,不需要暴露的属性或方法尽量设计为private类型。实现类若非必要,不要扩大父类中的访问权限。

模板方法模式的扩展

还是针对上面汽车启动的例子,如果需求变了,要求Car1启动不要鸣笛,Car2启动后需要鸣笛,那上面的模板方法就满足不了这个新需求了。该怎么办呢?我们可以通过钩子方法来解决。我们修改类图如下:

在父类中增加了一个isAlarm()方法,判断是否需要鸣笛,由子类去重写这个方法,这个isAlarm()方法就是钩子方法。修改后的父类代码如下:

然后对不需要鸣笛的Car1进行修改:

然后执行Client类,发现Car1启动后已经不会再鸣笛了,Car2不受影响。

模板方法模式的优缺点

优点

1. 封装不变部分,扩展可变部分。

2. 提取公共部分代码,便于维护。

3. 行为由父类控制,子类实现。

缺点

我们平常的设计习惯是抽象类负责声明最抽象、最一般的事物属性和方法,子类完成具体的事物和方法。但模板方法模式却颠倒了,抽象类定义了部分抽象方法,由子类实现,子类执行的结果影响了父类的结果,对于刚接触的同学来说,可能有点不适应。

模板方法模式的使用场景

1. 多个子类有共有的方法,并且逻辑基本相同。

2. 重要、重复的算法,可以把核心算法设计为模板方法,周边的相关细节功能则交给子类去实现。

3. 重构时,模板方法模式是一个经常使用的模式,把相同的代码抽取到父类中,然后通过钩子函数约束其行为。

点个关注吧,我会持续更新更多干货~~

  • 发表于:
  • 原文链接https://kuaibao.qq.com/s/20200303A0AR1000?refer=cp_1026
  • 腾讯「腾讯云开发者社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。

关注

腾讯云开发者公众号
10元无门槛代金券
洞察腾讯核心技术
剖析业界实践案例
腾讯云开发者公众号二维码

扫码关注腾讯云开发者

领取腾讯云代金券