目录
场景
原有逻辑
有何问题
解决方案
解决思路
代码实现
重写示例
模板方法的优缺点
模板方法的本质
何时选用
现在模拟一个场景,两个人要登录一个系统,一个是管理员一个是用户,这两个不同身份的登录是由后端对应的两个接口实现的,用户登录只需验证库里是否存在,管理员登录时需要验证加密后的密码是否与数据库数据一致。
这里大致说一下,创建了两个实体用来描述用户和管理员传来的用户名、密码。创建两个模块来分别处理用户和管理员的登录。
1.重复或相似代码太多2.扩展不方便(比如要添加同一个编号同时只能登录一次,那这两个登录模块都要修改)
模板方法
重复代码多、扩展不方便的原因在哪?就是因为没把那些相似的代码抽取出来做成公共的功能。
我们把具体的不同的步骤实现延迟到子类去实现,这样就可以通过子类来提供不同的功能实现了。
第一和第三个步骤是必不可少,第二个是可选的(可变的)。
先定义一个父类,并在内部提供一个方法来定义整个骨架。这个方法就是模板方法,然后把父类无法确定的实现,延迟到具体的子类来实现
肯定有一个携带了骨架方法的父类,不用想肯定是抽象类
package day14模板方法模式;
public abstract class AbstractClass {
/**
* 原语操作1,所谓原语操作就是抽象的操作,必须要由子类提供实现
*/
public abstract void doPrimitiveOperation1();
/**
* 原语操作2,所谓原语操作就是抽象的操作,必须要由子类提供实现
*/
public abstract void doPrimitiveOperation2();
/**
* 模板方法
*/
public final void templateMethod(){
doPrimitiveOperation1();
doPrimitiveOperation2();
}
}
具体实现
package day14模板方法模式;
/**
* 具体实现类,实现原语操作
*/
public class ConcreteClass extends AbstractClass{
@Override
public void doPrimitiveOperation1() {
}
@Override
public void doPrimitiveOperation2() {
}
}
我们要实现登录的合并,首先需要一个共同的参数接收类
package day14模板方法模式;
/**
* 封装进行登录控制所需要的数据
*/
public class LoginModel {
/**
* 登陆人员的编号,通用的,可能是用户也可能是管理员
*/
private String loginId;
/**
* 登录的密码
*/
private String pwd;
public String getLoginId() {
return loginId;
}
public void setLoginId(String loginId) {
this.loginId = loginId;
}
public String getPwd() {
return pwd;
}
public void setPwd(String pwd) {
this.pwd = pwd;
}
}
接下来定义公共的登录控制算法骨架
package day14模板方法模式;
/**
* 登录控制的模板
*/
public abstract class LoginTemplate {
public final boolean login(LoginModel lm){
LoginModel loginUser = this.findLoginUser(lm.getLoginId());
if (loginUser != null){
String pwd = this.encryptPwd(lm.getPwd());
lm.setPwd(pwd);
// 判断是否匹配的上
return this.match(lm,loginUser);
}
return false;
}
public boolean match(LoginModel lm, LoginModel loginUser){
if (lm.getLoginId().equals(loginUser.getLoginId()) && lm.getPwd().equals(loginUser.getPwd())){
return true;
}
return false;
}
/**
* 根据登陆编号来查找和获取存储中相应的数据
* @param loginId
* @return
*/
public abstract LoginModel findLoginUser(String loginId);
/**
* 对密码数据进行加密
*/
public String encryptPwd(String pwd){
return pwd;
}
}
实现用户登录控制的逻辑处理
package day14模板方法模式;
/**
* 普通用户登录控制的逻辑处理
*/
public class NormalLogin extends LoginTemplate {
@Override
public LoginModel findLoginUser(String loginId) {
// 这里省略具体的处理,仅作示意,返回一个有默认数据的对象
LoginModel loginModel = new LoginModel();
loginModel.setLoginId(loginId);
loginModel.setPwd("testPwd");
return loginModel;
}
}
管理员登录控制的逻辑处理
package day14模板方法模式;
/**
* 工作人员登录控制的逻辑处理
*/
public class WorkerLogin extends LoginTemplate {
@Override
public LoginModel findLoginUser(String loginId) {
// 这里省略具体的处理,仅作示意,返回一个有默认数据的对象
LoginModel loginModel = new LoginModel();
loginModel.setLoginId(loginId);
loginModel.setPwd("testPwd");
return loginModel;
}
@Override
public String encryptPwd(String pwd) {
System.out.println("使用MD5加密");
return super.encryptPwd(pwd);
}
}
Client
package day14模板方法模式;
public class Client {
public static void main(String[] args) {
// 准备登陆人的信息
LoginModel loginModel = new LoginModel();
loginModel.setLoginId("admin");
loginModel.setPwd("workerpwd");
// 准备用来判断的对象
WorkerLogin workerLogin = new WorkerLogin();
NormalLogin normalLogin = new NormalLogin();
// 进行登录测试
boolean login = workerLogin.login(loginModel);
System.out.println("管理员可以登录= " + login);
boolean login1 = normalLogin.login(loginModel);
System.out.println("用户可以登录=" + login1);
}
}
固定算法骨架
很好的体现了开闭原则和里氏替换原则