在面向对象开发过程中,,通常会遇到这样一个问题,我们知道一个算法所需的关键步骤,并确定了这些步骤的执行顺序,但是,某些步骤的具体实现是未知的,或者说某些步骤的实现是会随着环境改变的,例如,执行程序的流程大致如下:
1.检查代码的正确性;
2.链接相关的类库;
3.编译相关代码;
4.执行程序;
对于不同的语言,上面4个步骤可能是不一样的,但是,它们的执行流程是固定的,这类问题的解决方案就是模板方法模式。
定义:
定义一个操作中的算法的框架,而将一些步骤延迟到子类中,使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
使用场景:
1.多个子类有公有的方法,并且逻辑基本相同时。
2.重要、复杂的算法,可以把核心算法设计为模板方法,周边的相关细节功能则由各个子类实现。
3.重构时,模板方法模式是一个经常使用的模式,把相同的代码抽取到父类中,然后通过钩子函数约束其行为;
UML类图:
AbstractClass:抽象类,定义了一套算法框架;
ConcreteClass1:具体实现类A:
ConcreteClass2:具体实现类B
示例:
模板方法实际上是封装一个固定流程,就像是一套执行模板一样,第一步该做什么,第二步该做什么都已经在抽象类中定义好。而子类可以有不同的算法实现,在框架不被修改的情况下实现某些步骤的算法替换
举例计算机开机的过程
/**
* 抽象类,定义算法框架
* @author Administrator
*
*/
public abstract class AbstractComputer {
void powerOn(){
System.out.println("开记电源");
}
void checkHardware(){
System.out.println("硬件检查");
}
void loadOS(){
System.out.println("载入操作系统");
}
void login(){
System.out.println("验证账户");
}
/**
* 启动计算机方法,该方法为final,防止子类重写
*/
public final void startUp(){
System.out.println("----------");
powerOn();
checkHardware();
loadOS();
login();
System.out.println("----------");
}
//计算机的启动过程不会变动,但是不同的电脑种类实现可能各不相同,因此子类需要重定局一相应的方法来进行自定义处理是
//需要注意的是startUp为final方法,这样就保证了逻辑流程不能被子类修改,子类只能够改变某一步骤中的具体实现 ,
//这样就保证了这个逻辑流程的稳定性,startUp中的这向个算法步骤我们可以称为是一个套路,也称为模板方法
}
public class PersonComputer extends AbstractComputer{
@Override
void login() {
System.out.println("用户和密码验证");
}
}
public class MilitaryComputer extends AbstractComputer{
@Override
void checkHardware() {
System.out.println("检查各种安全流程");
}
@Override
void login() {
System.out.println("视网膜识别进入系统");
}
}
public class Client {
public static void main(String[] args) {
AbstractComputer computer = new PersonComputer();
computer.startUp();
computer = new MilitaryComputer();
computer.startUp();
}
}
----------
开记电源
硬件检查
载入操作系统
用户和密码验证
----------
运行结果:
----------
开记电源
检查各种安全流程
载入操作系统
视网膜识别进入系统
----------
总结:
模板方法可以概括为“流程封装”,也就是把某个固定的流程封装在一个final方法中,,并且让子类能够定制这个流程中的某些或者所有步骤,这就要求父类提取共用的代码,提升代码的复用率,同时也带来了更好的可扩展性。
优点:
1.封装不变部分,扩展可变部分。
2.提取公共部分代码,便于维护。
缺点:
模板方法会带来代码阅读的难度,会让用户觉得难以理解