从字面意义上理解, 模板方法就是定义出来一套方法, 作为模板, 也就是基础。 在这个基础上, 我们可以进行加工,实现个性化的实现。比如:一日餐三. 早餐, 中餐, 晚餐. 每个人都要吃三餐, 但每个人的三餐吃的可能都不一样. 一日三餐定义了模板--早中晚, 每个人的三餐就是模板的具体实现.
定义一个操作中的算法骨架,而将算法的一些步骤延迟到子类中,使得子类可以不改变该算法结构的情况下重定义该算法的某些特定步骤。它是一种类行为型模式。
第一步: 定义模板类 第二步: 定义具体子类 第三步: 客户端调用
下面来了解每一个步骤:
通常模板类是抽象类,负责给出算法的轮廓或者框架。他是有若干个模板方法和若干个基本方法构成。
具体子类,也就是具体的实现类, 实现抽象类中的抽象方法。他们是抽象的模板方法中一个组成部分。
客户端调用抽象类, 实例化的时候实例化具体类, 只需要调用抽象类的模板方法就可以了。
从图中可以看出抽象类的结构可以定义三类方法。 可以有一个也可以有多个。子类必须需要实现抽象类中的抽象方法,可以选择性重写父类的具体方法。子类实现接口的时候,要多思考设计模式的六大原则。
package com.lxl.www.designPatterns.templatePattern.template;
/**
* 抽象类, 定义模板
*/
public abstract class AbstractClass {
/**
* 定义模板方法
* 规范了流程的框架
*/
public void templateMethod() {
// 先调用具体方法
specificMethod();
// 在调用抽象方法
abstractMethod();
}
/**
* 具体方法
*/
public void specificMethod() {
// 具体的公共逻辑, 父子类通用
System.out.println("具体方法---父子类通用逻辑");
}
/**
* 抽象方法
*
* 抽象方法, 子类必须重写
*/
public abstract void abstractMethod();
}
在定义具体的实现类, 实现父类的抽象方法
package com.lxl.www.designPatterns.templatePattern.template;
/**
* 具体实现类
*/
public class ConcreteClass extends AbstractClass{
/**
* 重写父类的抽象方法
*/
@Override
public void abstractMethod() {
System.out.println("具体实现类--重写父类的抽象方法");
}
}
最后定义客户端调用
package com.lxl.www.designPatterns.templatePattern.template;
/**
* 模板方法客户端
*/
public class TemplateClient {
public static void main(String[] args) {
AbstractClass abstractClass = new ConcreteClass();
abstractClass.templateMethod();
}
}
运行结果:
具体方法---父子类通用逻辑 具体实现类--重写父类的抽象方法
对照模板方法设计模式,我们来看一个具体的案例。
每个人的一日安排都有三餐, 早餐, 中餐,晚参。 但每个人的三餐食物不尽相同,我们来看看每个人的三餐变化, 以及三餐前后要做的事情。
package com.lxl.www.designPatterns.templatePattern.oneDayArrangement;
/**
* 一日三餐抽象类
*/
public abstract class ArrangementAbstract {
/**
* 模板方法
* 规定了一天的框架
*/
public void templateMethod() {
System.out.println("一日安排如下: ");
getUp();
breakfast();
lunch();
dinner();
getDown();
}
public void getUp() {
System.out.println("起床");
}
public void getDown() {
System.out.println("睡觉");
}
/**
* 早餐抽象类
*/
public abstract void breakfast() ;
/**
* 午餐抽象类
*/
public abstract void lunch();
/**
* 晚餐抽象类
*/
public abstract void dinner();
}
定义一日三餐抽象类。每个人的日程安排都是,起床,早餐,中餐,晚餐,睡觉。 其中起床和睡觉是每个人都要做的事情,三餐也是,但三餐的食物不同,于是我们将三餐定义为抽象
一日安排实现类
package com.lxl.www.designPatterns.templatePattern.oneDayArrangement;
/**
* 张三的一日三餐安排
*/
public class PersonArrangement extends ArrangementAbstract{
private String name;
public PersonArrangement(String name) {
this.name = name;
}
/**
* 早餐抽象类
*/
public void breakfast(){
System.out.println(name + "--早餐吃牛奶面包");
}
/**
* 午餐抽象类
*/
public void lunch() {
System.out.println(name + "--中餐吃食堂");
}
/**
* 晚餐抽象类
*/
public void dinner() {
System.out.println(name + "--晚餐吃水果");
}
}
客户端调用
public class Client {
public static void main(String[] args) {
ArrangementAbstract zhangsan = new PersonArrangement("张三");
zhangsan.templateMethod();
}
}
运行结果:
一日安排如下: 起床 张三--早餐吃牛奶面包 张三--中餐吃食堂 张三--晚餐吃水果 睡觉
可以看出, 完全按照模板方法的步骤实现。
我们上面说了, 模板方法设计模式中, 基本方法包括抽象方法,具体方法和钩子方法. 如果能够使用好钩子方法, 可以在程序中完美实现子类控制父类的行为. 我们来看下面的案例:
我们在抽象方法中定义一个钩子方法hookMethod(), 在模板方法templateMethod()中,钩子方法控制了代码的流程.
UML图:
源代码:
package com.lxl.www.designPatterns.templatePattern.hookMethod;
/**
* 抽象类, 定义模板
*/
public abstract class AbstractClass {
/**
* 定义模板方法
* 规范了流程的框架
*/
public void templateMethod() {
// 调用具体方法
specificMethod();
// 钩子方法控制下一步骤
if (hookMethod()) {
// 调用抽象方法
abstractMethod();
}
}
/**
* 具体方法
*/
public void specificMethod() {
// 具体的公共逻辑, 父子类通用
System.out.println("具体方法---父子类通用逻辑");
}
/**
* 钩子方法
* 钩子方法是有具体实现的,
*/
public boolean hookMethod() {
return true;
}
/**
* 抽象方法
*
* 抽象方法, 子类必须重写
*/
public abstract void abstractMethod();
}
定义具体实现
/**
* 具体实现类
*/
public class ConcreteClass extends AbstractClass {
/**
* 重写父类的抽象方法
*/
@Override
public void abstractMethod() {
System.out.println("具体实现类--重写父类的抽象方法");
}
/**
* 钩子方法
* @return
*/
@Override
public boolean hookMethod() {
System.out.println("重写了父类的钩子方法, 反向控制父类的行为");
return false;
}
}
重写了钩子方法, 反向控制父类的行为
public class TemplateClient {
public static void main(String[] args) {
AbstractClass abstractClass = new ConcreteClass();
abstractClass.templateMethod();
}
}
运行结果
具体方法---父子类通用逻辑 重写了父类的钩子方法, 反向控制父类的行为
如果子类钩子方法 HookMethod() 的代码改变,则程序的运行结果也会发生改变。