1.什么是模板方法模式
模板模式(Template Pattern)这种类型的设计模式属于行为型模式。
定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。
模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤
自己的理解 父类定义一套模板 子类实现时候套模板直接使用
2.为什么要用模板方法模式
2.1 出现了重复的代码 提取公共的方法使用
2.2 需要规范具体流程 定义做事情的步骤
请看下面需求 通过实现提取公共方法 使用了模板设计模式
需求 现在有一个模具 可以生成不同品牌的汽车
代码如下
public class TemplatePattern { public static void main(String[] args) { Model bwm = new BWM(); bwm.run(); Model audi = new Audi(); audi.run(); } } /** * 抽象类 模具 可以制造不同品牌的汽车 * 三个抽象方法 * 点火启动 * 跑起来 * 刹车停止 */ abstract class Model { public abstract void start(); public abstract void run(); public abstract void stop(); } /** * 制造宝马 实现抽象模具 */ class BWM extends Model { @Override public void start() { System.out.println("宝马点火启动"); } @Override public void run() { this.start(); this.stop(); } @Override public void stop() { System.out.println("宝马刹车"); } } /** * 制造奥迪 实现抽象模具 */ class Audi extends Model { @Override public void start() { System.out.println("奥迪点火启动"); } @Override public void run() { this.start(); this.stop(); } @Override public void stop() { System.out.println("奥迪刹车"); } }
结果如图
发现问题
在子类 BWM 和 Audi 中 这段代码是相同的, 可以抽取成公共的方法
解决
将公共方法抽取到父类 子类使用时直接调用 直接删除子类原来的方法
代码如下
public class TemplatePattern { public static void main(String[] args) { Model bwm = new BWM(); bwm.run(); Model audi = new Audi(); audi.run(); } } /** * 抽象类 模具 可以制造不同品牌的汽车 * 三个抽象方法 * 点火启动 * 跑起来 * 刹车停止 */ abstract class Model { public abstract void start(); //由抽象方法变为具体方法 即模板方法 public void run(){ this.start(); this.stop(); }; public abstract void stop(); } /** * 制造宝马 实现抽象模具 */ class BWM extends Model { @Override public void start() { System.out.println("宝马点火启动"); } @Override public void stop() { System.out.println("宝马刹车"); } } /** * 制造奥迪 实现抽象模具 */ class Audi extends Model { @Override public void start() { System.out.println("奥迪点火启动"); } @Override public void stop() { System.out.println("奥迪刹车"); } }
结果如下
3.怎么使用模板方法模式
即将公共的方法或者做事情的步骤提取到父类 子类直接使用
在上面需求中,抽象模具类 即模板类 (官方说法抽象模板,子类为具体模板) run()方法是模板方法
在模板类中
两个抽象方法 即基本操作/基本方法
run()方法是模板方法 防止恶意操作一般是方法用final修饰 不允许被重写
还可以有一个钩子函数 即回调方法
例子1
/* * 抽象类 饮料机模板类 */ public class TemplatePattern2 { public static void main(String[] args) { System.out.println("开始煮咖啡"); Drink t1 = new Coffee(); t1.driveTemplate(); System.out.println("咖啡完成"); System.out.println("====================="); System.out.println("开始泡茶"); Drink t2 = new Tea(); t2.driveTemplate(); System.out.println("茶完成"); } } abstract class Drink{ //将水煮沸 abstract void boilWater(); //炮制饮料 public abstract void blew(); //倒入杯中 abstract void pourInCup(); //加入调味料 public abstract void addCondiments(); //制备饮料的模板方法 用final修饰,不能允许子类修改这个模板框架 public final void driveTemplate() { boilWater(); blew(); pourInCup(); addCondiments(); } } /* * 煮咖啡 */ class Coffee extends Drink { @Override void boilWater() { System.out.println("直饮水煮沸"); } //炮制咖啡 @Override public void blew() { System.out.println("用沸水冲泡咖啡"); } @Override void pourInCup() { System.out.println("倒入咖啡杯"); } //加入调味料 @Override public void addCondiments() { System.out.println("加入糖和牛奶"); } } /* * 泡茶 */ class Tea extends Drink { @Override void boilWater() { System.out.println("泉水煮沸"); } //冲泡茶 @Override public void blew() { System.out.println("用80度的热水浸泡茶叶5分钟"); } @Override void pourInCup() { System.out.println("倒入茶杯"); } //添加调料 @Override public void addCondiments() { System.out.println("加入柠檬"); } }
结果如下
针对例子1的新需求 泡茶的时候不想要添加任何东西,这时候我们使用上面的方法是没法完成的,
这时候我们就引出了钩子函数 我们可以使用钩子函数来判断是否要执行某一步的操作
例子2 修改例子1
模板类
增加钩子函数
模板方法增加钩子函数的判断
class Tea 中重写钩子函数 设置为false 不加料
*/ public class TemplatePattern3 { public static void main(String[] args) { System.out.println("开始煮咖啡"); Drink t1 = new Coffee(); t1.driveTemplate(); System.out.println("咖啡完成"); System.out.println("====================="); System.out.println("开始泡茶"); Drink t2 = new Tea(); t2.driveTemplate(); System.out.println("茶完成"); } } abstract class Drink{ //将水煮沸 abstract void boilWater(); //炮制饮料 public abstract void blew(); //倒入杯中 abstract void pourInCup(); //加入调味料 public abstract void addCondiments(); //制备饮料的模板方法 用final修饰,不能允许子类修改这个模板框架 public final void driveTemplate() { boilWater(); blew(); //钩子函数进行判定(例如茶不想加入调味料) if(isRight()){ //4. 进入调味料 addCondiments(); } } //钩子函数 判断用户是否要执行某些功能 public boolean isRight(){ return true; } } /* * 煮咖啡 */ class Coffee extends Drink { @Override void boilWater() { System.out.println("直饮水煮沸"); } //炮制咖啡 @Override public void blew() { System.out.println("用沸水冲泡咖啡"); } @Override void pourInCup() { System.out.println("倒入咖啡杯"); } //加入调味料 @Override public void addCondiments() { System.out.println("加入糖和牛奶"); } } /* * 泡茶 */ class Tea extends Drink { @Override void boilWater() { System.out.println("泉水煮沸"); } //冲泡茶 @Override public void blew() { System.out.println("用80度的热水浸泡茶叶5分钟"); } @Override void pourInCup() { System.out.println("倒入茶杯"); } //添加调料 @Override public void addCondiments() { System.out.println("加入柠檬"); } //重写钩子函数 改变其值 @Override public boolean isRight(){ return false; } }
结果如下