需求:编码实现汽车功能
现在基本上每家都有汽车,我们也都知道汽车都有以下功能:启动、停止、鸣笛、跑。知道了这些,我们就可以用代码去完成这个功能。
先设计类图如下:
我们定义了一个抽象父类,里面定义了公有的方法,然后两个子类去实现这些方法。
抽象父类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. 重构时,模板方法模式是一个经常使用的模式,把相同的代码抽取到父类中,然后通过钩子函数约束其行为。
点个关注吧,我会持续更新更多干货~~
领取专属 10元无门槛券
私享最新 技术干货