解释
定义一个操作中的算法的框架,而将一些步骤延迟到子类中。使得子类可以不改变一个算法的结构可重定义改算法的某些特定步骤(通俗理解:将方法的实现延迟到子类)
类图
应用场景
- 多个子类有公有的方法,并且逻辑基本相同
- 重要、复杂的算法,可以把核心算法设计为模版方法,周边的相关细节功能则由各个子类实现
- 重构时,模板方法模式是一个经常使用的模式,把相同的代码抽取到父类中,然后通过钩子函数(见下文解释)约束其行为
写法
- 声明抽象父类,抽象公有方法
- 延迟到子类实现抽象方法
- 通过钩子函数约束父亲行为(可选)
通用代码
/**
* Created by zs on 2017/3/17.
*
* 抽象模板类
*/
public abstract class AbstractClass {
//基本方法
protected abstract void doSomething();
//基本方法
protected abstract void doAnything();
public void templateMethod(){
//调用基本方法,完成相关逻辑
this.doAnything();
this.doSomething();
}
}
/**
* Created by zs on 2017/3/17.
*
* 具体模板类
*/
public class ConcreteClass1 extends AbstractClass {
@Override
protected void doSomething() {
//to do you work ...
}
@Override
protected void doAnything() {
//to do you work ...
}
}
/**
* Created by zs on 2017/3/17.
*
* 具体模板类
*/
public class ConcreteClass2 extends AbstractClass {
@Override
protected void doSomething() {
//to do you work ...
}
@Override
protected void doAnything() {
//to do you work ...
}
}
/**
* Created by zs on 2017/3/17.
*
* 场景类
*/
public class Client {
public static void main(String[] args) {
AbstractClass class1 = new ConcreteClass1();
AbstractClass class2 = new ConcreteClass2();
//调用模板方法
class1.templateMethod();
class2.templateMethod();
}
}
多说一句
抽象模板中的基本方法尽量设计为protected类型,符合迪米特法则(请看下文),不需要暴露的属性和方法尽量不要设置为protected类型,实现类若非必要,尽量不要扩大父类中的访问权限
那么什么是迪米特法则(LoD)呢:
通俗理解:一个类应该对自己需要耦合或调用的类知道的最少,你(被耦合或调用的类)的内部是如何复杂都和我没关系,那是你的事情,我就知道你提供的这么多的public 方法,我就调用这么多,其他的我一概不关系。
解释分析:
- 只和朋友交流 一个类只和朋友交流,不与陌生类交流
- 朋友间也是有距离的,迪米特法则要求类 “羞涩” 一点,尽量不要对外公布太多的public方法和非静态的public变量,尽量内敛
- 是自己的就是自己的,如果一个方法放在本类中,既不增加类间关系,也对本类不产生负面影响,那就放在本类中
扩展
问题:父类的模板方法约定了基本方法(父类抽象方法)的执行,但是子类无法私人订制是否要执行某一个基本方法?
解决方案:钩子方法
钩子方法:影响模板方法执行结果的方法
一言不合上代码:
/**
* Created by zs on 2017/3/17.
*
* 抽象模板类
*/
public abstract class AbstractClass {
//基本方法
protected abstract void doSomething();
//基本方法
protected abstract void doAnything();
//钩子方法
protected boolean isExecuteSomething(){
return true;
}
//模板方法
public void templateMethod(){
//调用基本方法,完成相关逻辑
this.doAnything();
//是否执行,由子类决定
if(isExecuteSomething()){
this.doSomething();
}
}
}
/**
* Created by zs on 2017/3/17.
*
* 具体模板类
*/
public class ConcreteClass1 extends AbstractClass {
@Override
protected void doSomething() {
// to do you work...
}
@Override
protected void doAnything() {
//to do you work ...
}
@Override
protected boolean isExecuteSomething() {
return false;
}
}
/**
* Created by zs on 2017/3/17.
* * 具体模板类
*/
public class ConcreteClass2 extends AbstractClass {
@Override
protected void doSomething() {
//to do you work ...
}
@Override
protected void doAnything() {
//to do you work ...
}
}
曲线救国
问题:父类怎么调用子类的方法?能?不能?
回答:能
解决方案:
- 把子类传递到父类的有参构造中,然后调用
- 使用反射的方式调用(你使用了反射还有谁不能差遣的,还有谁......还有谁......)
- 父类调用子类的静态方法
自嘲:这三种方法都是直接调用子类的方法,项目中允许使用吗?不允许!为什么要用父类调用子类的方法,如果一定要调用子类,那为什么要继承它呢?
换个姿势:父类建立框架,子类重写了父类部分的方法后,再调用父类继承的方法,产生不同的结果,这一修改子类,影响父类的行为,曲线救国的方式实现了父亲依赖子类的场景。
一言不合再上代码:
/**
* Created by zs on 2017/3/17.
*
* 抽象模板类
*/
public abstract class AbstractClass {
//基本方法
protected abstract void doSomething();
//基本方法
protected abstract void doAnything();
//模板方法
public void templateMethod(){
//调用基本方法,完成相关逻辑
this.doAnything();
this.doSomething();
}
}
/**
* Created by zs on 2017/3/17.
*
* 具体模板类
*/
public class ConcreteClass1 extends AbstractClass {
@Override
protected void doSomething() {
// to do you work...
//父类调用子类
sonMethod();
//to do you work...
}
@Override
protected void doAnything() {
//to do you work ...
}
//子类方法
private void sonMethod(){
// to do you work
}
}
优缺点
优点:
- 封装不变部分,扩展可变部分
- 提取公共部分代码,便于维护
- 行为由父类控制,子类实现
缺点:
子类执行的结果影响了父类的结果,也就是子类产生了影响,这在复杂的项目中,会带来代码阅读的难度