模板方法模式介绍
在面向对象开发过程中,通常会遇到这样一个问题,我没知道一个算法所需的关键步骤,并确定了这些步骤的执行顺序,但是某些步骤的具体实现是未知的,或者说某些步骤的实现是会随着环境的变化而改变的,例如,执行程序的流程大致如下:
(1) 检查代码的正确性;
(2) 链接相关的类库;
(3) 编译相关代码;
(4) 执行程序。
对于不同的程序设计语言,上述4个步骤都是不一样的,但是,它们的执行流程是固定的,这类问题的解决方案就是接下来要讲的模板方法模式。
模板方法模式的定义
定义一个操作中的算法的框架,而将一些步骤延迟到子类中,使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
模板方法模式的使用场景
(1) 多个子类有共有的方法,并且逻辑基本相同时。
(2) 重要、复杂的算法,可以把核心算法设计为模板方法,周边的相关细节功能则由各个子类实现。
(3) 重构时,模板方法模式是一个经常使用的模式,把相同的代码抽取到父类中,然后通过钩子函数约束其行为。
模板方法模式的UML类图
角色介绍:
- AbsTemplate:抽象类,定义了一套算法框架。
- ConcreteImplA:具体实现类A。
- ConcreteImplB:具体实现类B。
模板方法模式的简单示例
模板方法实际上市封装一个固定流程,就像是一套执行模板一样,第一步该做什么,第二步该做什么都已经在抽象类中定义好。而子类可以有不同的算法实现,在框架不被修改的情况下实现某些步骤的算法替换,下面以打开计算机这个动作来简单演示一下模板方法。打开计算机整个过程可简化为:启动计算机电源----检测硬件状态----载入计算机操作系统----用户登录,代码如下:
package com.guifa.abstractmethodpattern;
// 抽象的Computer
public abstract class AbstractComputer {
protected void powerOn() {
System.out.println("开启电源");
}
protected void checkHardware() {
System.out.println("硬件检查");
}
protected void loadOS() {
System.out.println("载入操作系统");
}
protected void login() {
System.out.println("小白的计算机无法验证,直接进入系统");
}
/**
* 启动计算机方法,步骤固定为开启电源、硬件检查、载入操作系统、用户登录。该方法为final,防止算法框架被覆写
*/
protected void startUp() {
System.out.println("----------开机START----------");
powerOn();
checkHardware();
loadOS();
login();
System.out.println("----------开机END------------");
}
}
package com.guifa.abstractmethodpattern;
/**
* 程序员的计算机
*/
public class CoderComputer extends AbstractComputer {
@Override
protected void login() {
System.out.println("程序员只需要进行用户和密码验证就可以了");
}
}
package com.guifa.abstractmethodpattern;
/**
* 军用计算机
*/
public class MilitaryComputer extends AbstractComputer {
@Override
protected void checkHardware() {
super.checkHardware();
System.out.println("检查硬件防火墙");
}
@Override
protected void login() {
System.out.println("进行指纹识别等复杂的用户验证");
}
}
package com.guifa.abstractmethodpattern;
public class Test {
public static void main(String[] args) {
AbstractComputer coderComputer = new CoderComputer();
coderComputer.startUp();
AbstractComputer militaryComputer = new MilitaryComputer();
militaryComputer.startUp();
}
}
输出结果如下:
----------开机START----------
开启电源
硬件检查
载入操作系统
程序员只需要进行用户和密码验证就可以了
----------关机END------------
----------开机START----------
开启电源
硬件检查
检查硬件防火墙
载入操作系统
进行指纹识别等复杂的用户验证
----------关机END------------
Process finished with exit code 0
通过上面例子可以看出,在startUp方法中有一些固定的步骤,依次为开启电源、检查硬件、加载系统、用户登录4个步骤,这4个步骤是计算机开机过程中不会变动的,但是不同用户的这几个步骤的实现可能各不相同,因此,子类需要覆写相应的方法来进行自定义处理,这里需要注意的是startUp为final方法,这样就保证了逻辑流程不会被子类修改,子类只能改变某一步骤中的具体实现,这样就保证了整个逻辑流程的稳定性。startUp中的这几个算法步骤我没可以称为一个套路,也称为模板方法,这也是该模式的由来。
总结
对模板方法模式用4个字概括就是:流程封装。也就是把某个固定的流程封装到一个final函数中,并且让子类能够定制这个流程中的某些或者所有步骤,这就要求父类提取公用的代码,提升代码的复用率,同时也带来了更好的扩展性。
优点
(1) 封装不变部分,扩展可变部分。
(2) 提取公共部分代码,便于维护。
缺点
模板方法会带来代码阅读的难度,会让用户觉得难以理解。