模板方法模式
package com;
abstract class AbstractClass {
public abstract void PrimitiveOperation1();
public abstract void PrimitiveOperation2();
public void templateMethod(){
PrimitiveOperation1();
PrimitiveOperation2();
}
}
****************
package com;
public class ConcreteClassA extends AbstractClass{
@Override
public void PrimitiveOperation1() {
// TODO Auto-generated method stub
System.out.println("A的具体方法1");
}
@Override
public void PrimitiveOperation2() {
// TODO Auto-generated method stub
System.out.println("A的具体方法2");
}
}
*******************
package com;
public class ConcreteClassB extends AbstractClass{
@Override
public void PrimitiveOperation1() {
// TODO Auto-generated method stub
System.out.println("B的具体方法1");
}
@Override
public void PrimitiveOperation2() {
// TODO Auto-generated method stub
System.out.println("B的具体方法2");
}
}
****************
package com;
public class Main {
public static void main(String[] args) {
// TODO Auto-generated method stub
AbstractClass c;
c=new ConcreteClassA();
c.templateMethod();
c=new ConcreteClassB();
c.templateMethod();
}
}
优点
实现代码复用
模板方法模式是一种实现代码复用的很好的手段。通过把子类的公共功能提炼和抽取,把公共部分放到模板里面去实现。
缺点
算法骨架不容易升级
模板方法模式最基本的功能就是通过模板的制定,把算法骨架完全固定下来。事实上模板和子类是非常耦合的,如果要对模板中的算法骨架进行变更,可能就会要求所有相关的子类进行相应的变化。所以抽取算法骨架的时候要特别小心,尽量确保是不会变化的部分才放到模板中。
本质
固定算法骨架。
应用场景
建议在如下情况中,选用模板方法模式:
- 需要固定定义算法骨架,实现一个算法的不变的部分,并把可变的行为留给子类来实现的情况
- 各个子类中具有公共行为,应该抽取出来,集中在一个公共类中去实现,从而避免代码重复
- 需要控制子类扩展的情况。模板方法模式会在特定的点来调用子类的方法,这样只允许在这些点进行扩展
相关模式
- 工厂方法模式和抽象工厂模式
这两个模式可以组合使用,具体的放到抽象工厂模式中去讲。 - 工厂方法模式和模板方法模式
这两个模式外观类似,都是有一个抽象类,然后由子类来提供一些实现,但是工厂方法模式的子类专注的是创建产品对象,而模板方法模式的子类专注的是为固定的算法骨架提供某些步骤的实现。
这两个模式可以组合使用,通常在模板方法模式里面,使用工厂方法来创建模板方法需要的对象。 -
例题:系统登录控制功能。普通用户登录前台和管理员登陆后台的登录控制 /** * 登录控制的模板方法需要的回调接口,需要把所有需要的接口方法都定义出来, * 或者说是所有可以被扩展的方法都需要被定义出来 */ public interface LoginCallback { /** * 根据登录编号来查找和获取存储中相应的数据 * @param loginId 登录编号 * @return 登录编号在存储中相对应的数据 */ public LoginModel findLoginUser(String loginId); /** * 对密码数据进行加密 * @param pwd 密码数据 * @param template LoginTemplate对象,通过它来调用在 * LoginTemplate中定义的公共方法或缺省实现 * @return 加密后的密码数据 */ public String encryptPwd(String pwd,LoginTemplate template); /** * 判断用户填写的登录数据和存储中对应的数据是否匹配得上 * @param lm 用户填写的登录数据 * @param dbLm 在存储中对应的数据 * @param template LoginTemplate对象,通过它来调用在 * LoginTemplate中定义的公共方法或缺省实现 * @return true表示匹配成功,false表示匹配失败 */ public boolean match(LoginModel lm,LoginModel dbLm ,LoginTemplate template); } /** * 封装进行登录控制所需要的数据 */ 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; } } /** * 登录控制的模板 */ public class LoginTemplate { /** * 判断登录数据是否正确,也就是是否能登录成功 * @param lm 封装登录数据的Model * @param callback LoginCallback对象 * @return true表示登录成功,false表示登录失败 */ public final boolean login(LoginModel lm,LoginCallback callback){ //1:根据登录人员的编号去获取相应的数据 LoginModel dbLm = callback.findLoginUser(lm.getLoginId()); if(dbLm!=null){ //2:对密码进行加密 String encryptPwd = callback.encryptPwd(lm.getPwd(),this); //把加密后的密码设置回到登录数据模型里面 lm.setPwd(encryptPwd); //3:判断是否匹配 return callback.match(lm, dbLm,this); } return false; } /** * 对密码数据进行加密 * @param pwd 密码数据 * @return 加密后的密码数据 */ public String encryptPwd(String pwd){ return pwd; } /** * 判断用户填写的登录数据和存储中对应的数据是否匹配得上 * @param lm 用户填写的登录数据 * @param dbLm 在存储中对应的数据 * @return true表示匹配成功,false表示匹配失败 */ public boolean match(LoginModel lm,LoginModel dbLm){ if(lm.getLoginId().equals(dbLm.getLoginId()) && lm.getPwd().equals(dbLm.getPwd())){ return true; } return false; } }