— 设计模式 — 1 min read
Define the skeleton of an algorithm in an operation,deferring some steps to subclasses.Template Method lets subclasses redefine certain steps of an algorithm without changing the algorithm's structure.(定义一个操作中的算法的框架,而将一些步骤延迟到子类中。使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。)
类型为模式
主要包含了两类方法:
多数情况下模板方法是固定的逻辑,也就是说不允许被子类修改,所以通常使用
final
修饰。
按照我们的设计习惯,抽象类负责声明最抽象、最一般的事物属性和方法,实现类完成具体的事物属性和方法。但是模板方法模式却颠倒了,抽象类定义了部分抽象方法,由子类实现,子类执行的结果影响了父类的结果,也就是子类对父类产生了影响,这在复杂的项目中,会带来代码阅读的难度,而且也会让新手产生不适感。
初级程序员在写程序的时候经常会问高手“父类怎么调用子类的方法”。这个问题很有普遍性,反正我是被问过好几回,那么父类是否可以调用子类的方法呢?我的回答是能,但强烈地、极度地不建议这么做,那该怎么做呢?
把子类传递到父类的有参构造中,然后调用。
使用反射的方式调用,你使用了反射还有谁不能调用的?!
父类调用子类的静态方法。
这三种都是父类直接调用子类的方法,好用不?好用!解决问题了吗?解决了!项目中允许使用不?不允许!我就一直没有搞懂为什么要用父类调用子类的方法。如果一定要调用子类,那为什么要继承它呢?搞不懂。其实这个问题可以换个角度去理解,父类建立框架,子类在重写了父类部分的方法后,再调用从父类继承的方法,产生不同的结果(而这正是模板方法模式)。这是不是也可以理解为父类调用了子类的方法呢?你修改了子类,影响了父类行为的结果,曲线救国的方式实现了父类依赖子类的场景,模板方法模式就是这种效果。
源自:《设计模式之禅》第10章模板方法模式-最佳实践
1abstract class AbstractComputer {2 /**3 * final禁止被重新定义4 */5 final protected void startComputer(){6 power();7 mainBoard();8 cpu();9 hardDisk();10 if(isDisplay()){11 display();12 }13 }14
15 final public void power(){16 System.out.println("开始供电");17 }18
19 /**20 * 抽象操作必须被重新定义21 */22 public abstract void mainBoard();23
24 public abstract void cpu();25
26 public abstract void hardDisk();27
28 public abstract void display();29 /**30 * hook函数可以被重新定义,默认返回 true,所有设备都开启显示31 */32 public boolean isDisplay(){33 return true;34 }35}
1class MacBook extends AbstractComputer{2
3
4 @Override5 public void mainBoard() {6 System.out.println("MacBook启动主板");7 }8
9 @Override10 public void cpu() {11 System.out.println("MacBook开始为CPU供电");12 }13
14 @Override15 public void hardDisk() {16 System.out.println("MacBook开始为硬盘供电");17 }18
19 @Override20 public void display() {21 System.out.println("MacBook屏幕点亮");22 }23}
1class IPad extends AbstractComputer{2
3 private boolean flag = true;4 @Override5 public void mainBoard() {6 System.out.println("IPad启动主板");7 }8
9 @Override10 public void cpu() {11 System.out.println("IPad开始为CPU供电");12 }13
14 @Override15 public void hardDisk() {16 System.out.println("IPad开始为硬盘供电");17 }18
19 @Override20 public void display() {21 System.out.println("IPad屏幕点亮!");22 }23
24 @Override25 public boolean isDisplay(){26 return this.flag;27 }28 /**29 * 用户自定义是否点亮屏幕30 */31 public void setDisplay(boolean userFlag){32 if (!userFlag){33 System.out.println("用户要求 iPad 屏幕不点亮");34 }35 this.flag = userFlag;36 }37}
1public class TemplateMethodPattern {2 public static void main(String[] args) {3 MacBook macBook = new MacBook();4 IPad iPad = new IPad();5 macBook.startComputer();6 System.out.println("-----------");7 iPad.setDisplay(false);8 iPad.startComputer();9 }10}
1开始供电2MacBook启动主板3MacBook开始为CPU供电4MacBook开始为硬盘供电5MacBook屏幕点亮6-----------7用户要求 iPad 屏幕不点亮8开始供电9IPad启动主板10IPad开始为CPU供电11IPad开始为硬盘供电
模板方法在一些开源框架中经常使用,他们通常用来进行功能拓展和定制化,通过继承一个抽象类,然后重写指定方法即可。在进行一些老旧代码重构时也可以考虑使用模板方法,抽出公共方法到父类,主要固定逻辑都交由父类,方便日后进行功能拓展,以及精简代码量。
设计模式 设计模式之禅