模板方法(Template Method)模式的定义如下:定义一个操作中的算法骨架,而将算法的一些步骤延迟到子类中,使得子类可以不改变该算法结构的情况下重定义该算法的某些特定步骤。它是一种类行为型模式。
模板方法模式用了类的继承机制,是一种应用广泛的模式,我们在平常项目中都会使用,只是没有想到是这个名字而已。
模板方法模式由抽象类与具体模板构成,抽象类也称为抽象模板,其中的方法有:
1.模板方法:可以有一个或几个,一般是一个具体方法,也就是一个框架,实现对基本方法的调度,完成固定的逻辑。
为防止恶意操纵,一般模板方法都加上final关键字,不允许重载。
2.基本方法:由子类实现,用于被模板方法调用。
具体模板用于实现父类定义的抽象方法。
优缺点:
优点:
封装不变部分,扩展可变部分:父类封装了具体流程以及实现部分不变行为,其它可变行为交由子类进行具体实现。
父类控制,子类实现:框架流程由父类限定,子类无法更改;子类可以针对流程某些步骤进行具体实现。
提取公共代码,便于维护。
缺点:
抽象规定了行为,具体负责实现,与我们平常设计习惯相反,难以理解。
举个例子
模拟登录的过程
普通用户是先注册,然后再在登录界面填写账号密码,后台比对,登录成功。
管理员询问是否开放注册,直接在登录界面填写账号密码,后台比对,登录成功。
模板方法类
public abstract class AbstractLogin {
//注册
protected abstract void register();
//填写账号密码
protected abstract void write();
//后台比对账号密码
protected abstract void findUser();
//登录成功
protected abstract void success();
//钩子方法,判断用户与管理员,默认用户
protected boolean isUser(){
return true;
}
//模板方法,定义算法骨架
public final void login(){
if(isUser())
this.register();
this.write();
this.findUser();
this.success();
}
}
用户登录类(具体模板)
public class UserLogin extends AbstractLogin {
@Override
protected void register() {
System.out.println("用户注册。。。");
}
@Override
protected void write() {
System.out.println("用户填写账号密码。。。");
}
@Override
protected void findUser() {
System.out.println("后台查询是否有此用户。。。");
}
@Override
protected void success() {
System.out.println("用户登录成功。。。");
}
}
管理员登录类(具体模板)
package factory.template;
public class AdminLogin extends AbstractLogin {
private boolean loginFlag = false;
@Override
protected void register() {
System.out.println("管理员注册。。。");
}
@Override
protected void write() {
System.out.println("管理员填写账号密码。。。");
}
@Override
protected void findUser() {
System.out.println("后台查询是否有此管理员。。。");
}
@Override
protected void success() {
System.out.println("管理员成功登录。。。");
}
@Override
protected boolean isUser() {
return this.loginFlag;
}
public void setLoginFlag(boolean flag){
this.loginFlag = flag;
}
}
测试类
import java.util.Scanner;
public class Client {
public static void main(String[] args) {
System.out.println("用户登录:");
UserLogin login = new UserLogin();
login.login();
System.out.println("管理员登录");
AdminLogin adminlogin = new AdminLogin();
System.out.println("是否为开放管理员注册?0是,1否");
int flag = new Scanner(System.in).nextInt();
if(flag == 0)
adminlogin.setLoginFlag(true);
adminlogin.login();
}
}
为什么这里不能用接口呢,是因为接口不能有方法体,模板方法根本写不了,通常在“既要约束子类的行为,又要为子类提供公共功能”的时候使用抽象类。
钩子操作:在模板方法中定义,并提供默认实现的操作。这些方法通常被视为可扩展的点,但不是必须的,子类可以有选择的覆盖这些方法,以提供新的实现来扩展功能。比如:模板方法中定义了5步操作,但是根据需要,某一种具体的实现只需要其中的1、2、3这几个步骤,因此它就只需要覆盖实现1、2、3这几个步骤对应的方法。那么4和5步骤对应的方法怎么办呢,由于有默认实现,那就不用管了。也就是说钩子操作是可以被扩展的点,但不是必须的。