场景
模板模式适合:父类控制业务处理流程,子类负责业务逻辑具体处理逻辑,换一种说法就是父类控制算法主流程,子类实现个性化步骤。典型案例就是spring的JdbcTemplate。
demo
假设有个场景:计算不同环境下产品的保质期时间。整个业务流程:
1. 预处理产品输入时间;
2. 计算常温状态产品保质期;
3. 个性化处理特殊场景下保质期。
定义抽象类,控制业务处理流程:
public abstract class AbstractEffectExpireCalc {
public CalcResult calc(CalcRequest input){
CalcRequest inputCanbeUse = input;
// 预处理产品的生产日期
prepareCalc(inputCanbeUse);
// 常规计算
CalcResult result = regularClac(inputCanbeUse);
// 不同存储条件下计算产品保质期
specicalCalc(inputCanbeUse, result);
return result;
}
abstract void prepareCalc(CalcRequest inputCanbeUse);
abstract void specicalCalc(CalcRequest inputCanbeUse, CalcResult result);
private CalcResult regularClac(CalcRequest inputCanbeUse) {
CalcResult result = new CalcResult();
result.setEffectTime(inputCanbeUse.getCreateTime());
return result;
}
}
2个子类实现:
public class RegularEnvCalc extends AbstractEffectExpireCalc{
@Override
void prepareCalc(CalcRequest inputCanbeUse) {
System.out.println("常温环境-不做预处理");
}
@Override
void specicalCalc(CalcRequest inputCanbeUse, CalcResult result) {
result.setExpireTime(inputCanbeUse.getCreateTime() + 10000);
}
}
public class ColdEnvCalc extends AbstractEffectExpireCalc {
@Override
void prepareCalc(CalcRequest inputCanbeUse) {
System.out.println("寒冷环境-不做预处理");
}
@Override
void specicalCalc(CalcRequest inputCanbeUse, CalcResult result) {
result.setExpireTime(inputCanbeUse.getCreateTime()+ 20000);
}
}
CalcRequest:
public class CalcRequest implements Serializable{
private static final long serialVersionUID = 4444396583894965214L;
/**
* 生产日期
*/
private long createTime;
public CalcRequest(){}
public long getCreateTime() {
return createTime;
}
public void setCreateTime(long createTime) {
this.createTime = createTime;
}
@Override
public String toString() {
return "CalcRequest{" +
"createTime=" + createTime +
'}';
}
}
CalcResult:
public class CalcResult implements Serializable {
private static final long serialVersionUID = -8600570365855077191L;
/**
* 保质期-生效时间
*/
private long effectTime;
/**
* 保质期过期时间
*/
private long expireTime;
public CalcResult() {
}
public long getEffectTime() {
return effectTime;
}
public void setEffectTime(long effectTime) {
this.effectTime = effectTime;
}
public long getExpireTime() {
return expireTime;
}
public void setExpireTime(long expireTime) {
this.expireTime = expireTime;
}
@Override
public String toString() {
return "CalcResult{" +
"effectTime=" + effectTime +
", expireTime=" + expireTime +
'}';
}
}
Test:
public class EffectExpireTest {
public static void main(String[] args){
CalcRequest input = new CalcRequest();
input.setCreateTime(1L);
AbstractEffectExpireCalc calc = new RegularEnvCalc();
CalcResult result = calc.calc(input);
System.out.println("常温环境保质期:" + result.getEffectTime() + "-" + result.getExpireTime());
calc = new ColdEnvCalc();
result = calc.calc(input);
System.out.println("寒冷环境保质期:" + result.getEffectTime() + "-" + result.getExpireTime());
}
}
优化
模板模式不是很复杂,但是运用中有个小缺点,如果子类实现很多,那就会造成子类的泛滥。针对这种场景,有大佬提出了解决方法,通过传入回调函数原本有子类实现的具体流程。
EffectExpireCalc:
public class EffectExpireCalc {
public CalcResult calc(CalcRequest input, PrepareCalcCallBack prepareAction, SpecicalCalcCallBack specialAction){
CalcRequest inputCanbeUse = input;
// 预处理产品的生产日期
prepareAction.prepareCalc(inputCanbeUse);
// 常规计算
CalcResult result = regularClac(inputCanbeUse);
// 不同存储条件下计算产品保质期
specialAction.specicalCalc(inputCanbeUse, result);
return result;
}
private CalcResult regularClac(CalcRequest inputCanbeUse) {
CalcResult result = new CalcResult();
result.setEffectTime(inputCanbeUse.getCreateTime());
return result;
}
}
PrepareCalcCallBack :
public interface PrepareCalcCallBack {
void prepareCalc(CalcRequest inputCanbeUse);
}
SpecicalCalcCallBack :
public interface SpecicalCalcCallBack {
void specicalCalc(CalcRequest inputCanbeUse, CalcResult result);
}
Test:
public class EffectExpireTest {
public static void main(String[] args){
CalcRequest input = new CalcRequest();
input.setCreateTime(1L);
EffectExpireCalc calc = new EffectExpireCalc();
CalcResult result = calc.calc(input, new PrepareCalcCallBack(){
@Override
public void prepareCalc(CalcRequest inputCanbeUse) {
System.out.println("常温环境-不做预处理");
}
}, new SpecicalCalcCallBack(){
@Override
public void specicalCalc(CalcRequest inputCanbeUse, CalcResult result) {
result.setExpireTime(inputCanbeUse.getCreateTime() + 10000);
}
});
System.out.println("常温环境保质期:" + result.getEffectTime() + "-" + result.getExpireTime());
result = calc.calc(input, new PrepareCalcCallBack(){
@Override
public void prepareCalc(CalcRequest inputCanbeUse) {
System.out.println("寒冷环境-不做预处理");
}
}, new SpecicalCalcCallBack(){
@Override
public void specicalCalc(CalcRequest inputCanbeUse, CalcResult result) {
result.setExpireTime(inputCanbeUse.getCreateTime()+ 20000);
}
});
System.out.println("寒冷环境保质期:" + result.getEffectTime() + "-" + result.getExpireTime());
}
}
可以看出虽然回调函数减少了子类泛滥,但是该写的代码还是没少啥,关键是结构没有用子类实现那么清晰了,所以用的时候也要权衡下。