引言
无论是现实生活还是实际开发中,我们常常会遇到一类相似的行为,他们都包含相似的基本操作和固定的流程,不同的是他们在不同的业务场景下,这些基本操作的具体实现有所不同,但是执行流程模式都是相同,当然最简单的话我们针对不同的业务区实现对应的基本操作,但那是很low的,代码质量堪忧,明明是重复的代码就没有必要存在了,模板方法模式就是解决这样的问题,同时还可以让后面接手的开发同学用最少的代码、最简单的方式来复用并实现更多的业务。
一、模板方法概述
模板方法模式是一种类的行为型模式,在它的结构图中只有类之间的继承关系,没有对象关联关系,模板方法模式(Template Method Pattern)官方定义:定义一个操作中的算法的框架,而将一些步骤延迟到子类中,使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。(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.)是不是很装X,通俗来说模板方法模式本质仅仅是使用了Java的继承机制,封装几个抽象方法和一个具体模板方法。模板方法模式中作为父类的抽象类叫做抽象模板AbstractClass,抽象模板中的基本方法尽量设计为protected类型,符合迪米特法则,不需要暴露的属性或方法尽量不要设置为protected类型。实现类若非必要,尽量不要扩大父类中的访问权限;继承抽象模板的就叫做具体模板,抽象模板中包含三种类型的方法: 基本方法、模板方法和钩子方法(Hook Method)。
基本方法——基本方法也叫做基本操作,是由子类实现的方法,并且在模板方法被调用。
模板方法——核心方法,不允许子类重写,所以都会加上final修饰符,可以有一个或几个,一般是一个具体方法框架,按照固定的流程对基本方法的调度
钩子方法——为了让模板方法的执行结果的更好地适应因外界条件改变。比如说银行办理业务为例,办理业务是个模板方法,普通人要经历排队、取号、等待、办理四个基本流程,而Vip则不需要排队、取号、等待,那么在设计的时候我们的抽象模板类需要考虑到,于是乎你得需要在模板方法各基本方法调用之前增加条件判断,那么用于设置这个条件的方法就是,钩子方法(Hook Method),钩子方法也可以是抽象的还可以由子类的一个方法返回值决定公共部分的执行结果。
二、模板方法的优点和缺点及常见可用场景
1、模板方法模式的优点
良好的扩展性,封装不变部分,扩展可变部分,把认为是不变部分的算法封装到父类实现,而可变部分的则可以通过继承来继续扩展。例如增加一个新的功能很简单,只要再增加一个子类,实现父类的基本方法就可以了。
提取公共部分代码,便于维护,减小维护升级成本,基本操作由父类定义,子类实现
基本方法是由子类实现的,因此子类可以通过扩展的方式增加相应的功能,符合开闭原 则。
2、模板方法模式的缺点
通常抽象类是负责声明某一类的事物的共同属性和抽象方法,实现类则是完成定义具体的特性和方法。但是模板方法模式却颠倒了,抽象类定义了部分抽象方法,由子类实现,子类执行的结果影响了父类的结果,也就是子类对父类产生了影响,可能会让新手产生不适感,以下不理解代码。
3、适合使用模板方法模式的场景
多个子类有公有的方法,并且逻辑基本相同时。
重要、复杂的算法,可以把核心算法设计为模板方法,周边的相关细节功能则由各个 子类实现。
重构时,模板方法模式是一个经常使用的模式,把相同的代码抽取到父类中,然后通过钩子方法约束其行为。
三、模板方法的实现
如下图所示模板方法模式很简单,通俗来说模板方法本质上封装了一个固定的工作流程,相似的一类事务都按照固定的流程来执行,整个结构就全部在一个抽象模板类里
前面所说模板方法模式是基于继承的代码复用基本技术,在模板方法模式中,可以将相同的代码放在父类中,而将不同的方法实现放在不同的子类中。所以实现起来步骤很简单:
1、定义抽象模板类
将部分逻辑以具体方法以及具体构造函数的形式实现,然后声明一些抽象方法来让子类实现剩余的逻辑,并且定义钩子方法控制基本方法的执行。
public abstract class AbstractClass {
//基本方法1
protected abstract void doOneStep();
//基本方法2
protected abstract void doSecStep();
//基本方法3
protected abstract void doThirdStep();
//模板方法,为了避免被子类覆写改变模板方法的算法骨架一般使用final修饰,可以有N个模板方法
public final void work(){
/*
* 调用基本方法,完成相关的逻辑
*/
if(oneStepNeedRun){//钩子方法控制基本方法是否执行
this.doOneStep();
}
this.doSecStep();
this.doThirdStep();
}
//钩子方法它在抽象类中不做事或者是默认的事情,子类可以选择覆盖它,可以有N个
protected boolean oneStepNeedRun(){
return true;
}
}
2、继承抽象模板类实现具体模板类
就是普通的继承,根据各自的业务需要可以选择是否覆写钩子方法的实现逻辑。
public class ConcreteClass extends AbstractClass {
//实现基本方法
protected void doOneStep() {
//业务逻辑处理
}
protected void doSecStep() {
//业务逻辑处理
}
protected void doThirdStep() {
//业务逻辑处理
}
}
测试
public class TemplateMethod {
public static void main(String[] args) {
AbstractClass obj = new ConcreteClass();//多态构建
//调用模板方法
obj.work();
}
}
小结
模板方法模式是一种类的行为型模式,在它的结构图中只有类之间的继承关系,没有对象关联关系。
模板方法模式是基于继承和多态的代码复用基本技术
在模板方法模式中,将固定的相同的功能的代码段放在父类并在父类中实现,而将需要根据业务而各自实现的方法统一抽象到父类而把实现延迟到不同的子类中。
在模板方法模式中,结构很简单一个抽象类和若干个具体实现子类,抽象类即抽象模板只是定义了基本的操作方法(抽象模板中的基本方法尽量设计为protected类型,符合迪米特法则,不需要暴露
的属性或方法尽量不要设置为protected类型。实现类若非必要,尽量不要扩大父类中的访问
权限。)而把实现延迟到子类中实现、以及实现了用于规范基本方法调用流程的模板方法(一般将模板方法声明为final),最后还可以根据具体的业务区定义钩子方法用于控制基本方法的执行。最后策略模式和模板方法模式都是用于封装算法,前者是利用组合和委托模型,而后者则是继承。