23种设计模式之模板方法模式
参考资料
- Java设计模式:23种设计模式全面解析(超级详细)
- 韩顺平老师的Java设计模式(图解+框架源码剖析)
- 秦小波老师的《设计模式之禅》
下文如有错漏之处,敬请指正
一、简介
定义
定义一个算法的框架,封装步骤相同的部分,将特定(不同)步骤的部分交由子类完成,以此达到代码复用。
特点
- 模板模式是一种行为型模式
- 模板方法模式就是抽象类与具体子类之间的协作(继承机制)
优点
-
封装了不变部分,扩展可变部分
把认为是不变部分的算法封装到父类中实现,而把可变部分算法由子类继承实现,便于子类继续扩展。
-
提取了公共的部分代码,便于代码复用。
-
行为由父类控制,子类实现
基本方法是由子类实现的,因此子类可以通过扩展方式增加相应的功能,符合开闭原则。
缺点
- 父类中的抽象方法由子类实现,子类执行的结果会影响父类的结果,这导致一种反向的控制结构,它提高了代码阅读的难度。
通用类图
-
AbstractClass
抽象模板
定义一个算法的骨架它由一个模板方法和若干个基本方法构成。
-
模板方法
模板方法定义了算法的骨架,按某种顺序调用基本方法。
-
基本方法
基本方法也叫做基本操作(特定步骤),是由子类实现的方法,并且在模板方法被调用。
基本方法包含以下几种类型:
- 具体方法:在抽象类中已经实现,在具体子类中可以继承或重写它。
- 抽象方法:在抽象类中定义,由具体子类实现特定步骤的内容。
- 钩子方法:在抽象类中定义,由具体子类实现用于控制父类的行为。
-
-
ConcreteClass
具体模板
实现抽象模板类中所定义的一个或多个抽象方法。
应用场景
- 多个子类有公有的方法,并且逻辑基本相同时,可以使用模板方法模式,抽取公有方法为模板方法。
- 算法的整体步骤固定,但其中个别部分易变时,可以使用模板方法模式,将容易变的部分抽象出来,由子类实现。
二、模板方法模式
需求:
把某个XX装入YY容器分为四个步骤,第一个步骤是准备YY容器,父类默认使用冰箱作为容器,第二个步骤“打开冰箱”和最后一个步骤“关闭冰箱”步骤内容相同,第三个步骤是将XX装入YY,因为XX不同,因此子类需要重写第三个步骤。
AbstractClass:抽象模板类,定义算法的骨架。
ConcreteClass:具体模板类,实现抽象类的所有抽象方法。ConcreteBowl(放碗)、ConcreteElephant(放大象),ConcreteAir(放空气)
AbstractClass:
package templateMethod;
public abstract class AbstractClass {
private String container="冰箱";
protected void setContainer(String container) {
this.container = container;
}
public void TemplateMethod() //模板方法
{
// 如果子类不使用容器的话,则直接进入最后一个步骤
if (HookMethod()) {
SpecificMethod1();
abstractMethod();
SpecificMethod2();
}else {
abstractMethod();
}
}
//钩子方法,限制父类的行为。父类默认使用冰箱,当子类修改为false,则不使用容器。
protected boolean HookMethod() {
return true;
}
// 具体方法
protected void SpecificMethod1() {
System.out.println("打开"+container);
}
protected void SpecificMethod2() {
System.out.println("关闭"+container);
}
// 抽象方法
protected abstract void abstractMethod();
protected String getContainer() {
return container;
}
}
ConcreteElephant:
package templateMethod;
public class ConcreteElephant extends AbstractClass {
@Override
protected void abstractMethod() {
System.out.println("将大象装入"+this.getContainer());
}
}
ConcreteBowl:
package templateMethod;
public class ConcreteBowl extends AbstractClass {
public ConcreteBowl() {
this.setContainer("橱柜");
}
@Override
protected void abstractMethod() {
System.out.println("将碗装入"+this.getContainer());
}
}
ConcreteAir:
package templateMethod;
public class ConcreteAir extends AbstractClass {
public ConcreteAir() {
this.setContainer("空气");
}
// 不使用容器
@Override
protected boolean HookMethod() {
return false;
}
@Override
protected void abstractMethod() {
System.out.println("将空气装入"+this.getContainer());
}
}
Client:
package templateMethod;
public class Client {
public static void main(String[] args) {
AbstractClass elephant = new ConcreteElephant();
elephant.TemplateMethod();
System.out.println("========================");
AbstractClass bowl = new ConcreteBowl();
bowl.TemplateMethod();
System.out.println("========================");
AbstractClass air = new ConcreteAir();
air.TemplateMethod();
/**
* 输出结果:
* 打开冰箱
* 将大象装入冰箱
* 关闭冰箱
* ========================
* 打开橱柜
* 将碗装入橱柜
* 关闭橱柜
* ========================
* 将空气装入空气
*/
}
}
三、总结
模板方法模式在一些开源框架中应用非常多,它提供了一个抽象类,然后开源框架写了一堆子类。在《××× In Action》中就说明了,如果你需要扩展功能,可以继承这个抽象类,覆写protected方法,然后调用一个类似execute方法,就可以完成你的扩展开发。