模板方法模式
文章目录
1.简介
# 模板方法模式
属于23种设计模式中的行为型模式。在抽象类中公开定义了执行的方法,子类可以按需重写其方法,但是要以抽象类中定义的方式调用方法。
总结起来就是:定义一个操作的算法结构,而将一些步骤延迟到子类中。在不改变算法结构的情况下,子类能重定义该算法的特定步骤
# 优点
1.封装不变的逻辑,扩展差异化的逻辑;
2.抽取公共代码,提高代码的复用性;
3.父类控制行为,子类实现细节。
# 缺点
1.不同的实现都需要一个子类去维护,会导致子类的个数不断增加,造成系统更加庞大。
# 总结:将公用的方法抽取到父类,在父类中预留可变的方法,最后子类去实现可变的方法。
# 案例
需要给不同的管理人员计算各种不同的津贴,如区域总监有区域管理津贴、佣金、培养育成津贴等等。通过分析,每种不用类型的津贴,都是需要金额x比例x系数,比例每种津贴都有不同的计算方式,系数也是。所以,大致的想法就是:金额x比例x系数这个计算方式设置为统一的方法,系数和比例让具体的津贴子类去实现。
2.类图
3.具体实现
3.1.抽象类(定义统一计算方法)
package com.hrfan.java_se_base.pattern.template_pattern;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.math.BigDecimal;
import java.util.Objects;
/**
* 需要给不同的管理人员计算各种不同的津贴,如区域总监有区域管理津贴、佣金、培养育成津贴等等。
* 通过分析,每种不用类型的津贴,都是需要金额x比例x系数,比例每种津贴都有不同的计算方式,系数也是。
* 所以,大致的想法就是:金额x比例x系数这个计算方式设置为统一的方法,系数和比例让具体的津贴子类去实现。
* 父类提供具体的计算逻辑
* 子类继承父类 父类计算逻辑中的值 进行替换(相当于替换文本中的字符)
* 主管工资计算
*/
public abstract class AbstractManageAllowanceCalService {
private final Logger logger = LoggerFactory.getLogger(AbstractManageAllowanceCalService.class);
public BigDecimal calAmount(BigDecimal amount){
// 如果传入对象为空 那么返回空
if (Objects.isNull(amount)){
return BigDecimal.ZERO;
}
BigDecimal ratio = getRatio();
BigDecimal coefficient = getCoefficient();
logger.error("金额 {}, 系数 {} 比例 {}",amount,coefficient,ratio );
return amount.multiply(coefficient).multiply(ratio);
}
/**
* 获取比例
*/
protected abstract BigDecimal getRatio();
/**
* 获取系数
*/
protected abstract BigDecimal getCoefficient();
}
3.2.定义主管的计算方法
package com.hrfan.java_se_base.pattern.template_pattern;
import org.springframework.stereotype.Service;
import java.math.BigDecimal;
import java.math.RoundingMode;
/**
* 主管公司计算类
* 工人
*/
@Service
public class SupervisorSalaryCalculationService extends AbstractManageAllowanceCalService{
/**
* 主管工资比例
* @return
*/
@Override
protected BigDecimal getRatio() {
return new BigDecimal("0.45").setScale(2, RoundingMode.HALF_UP);
}
/**
* 主管工资系数
* @return
*/
@Override
protected BigDecimal getCoefficient() {
return new BigDecimal("0.88").setScale(2, RoundingMode.HALF_UP);
}
}
3.3.定义工人的计算方法
package com.hrfan.java_se_base.pattern.template_pattern;
import org.springframework.stereotype.Service;
import java.math.BigDecimal;
import java.math.RoundingMode;
/**
* 工人计算薪资方法
*/
@Service
public class WorkerSalaryCalculationService extends AbstractManageAllowanceCalService{
/**
* 主管工资比例
* @return
*/
@Override
protected BigDecimal getRatio() {
return new BigDecimal("0.45").setScale(2, RoundingMode.HALF_UP);
}
/**
* 主管工资系数
* @return
*/
@Override
protected BigDecimal getCoefficient() {
return new BigDecimal("0.66").setScale(2, RoundingMode.HALF_UP);
}
}
4.测试类
package com.hrfan.java_se_base.pattern.template_pattern;
import com.hrfan.java_se_base.config.ResultObject;
import com.hrfan.java_se_base.pattern.template_pattern.params.SalaryParams;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.Objects;
/**
* 薪资计算测试类
* 实现了扩展applicationContextAware接口,解决静态方法无法获取bean实例调用其方法的问题?
* 我们发现在静态的Test方法中调用注入的bean直接报错,
* 这里解释一下:归功于类的加载机制与加载顺序,静态属性与静态代码块最先加载(static静态优先),这里加载静态方法是没有bean实例给你用的,自然会报错。
*/
@RestController
@RequestMapping("/v1/client")
public class SalaryController implements ApplicationContextAware {
/**
* 将上下文静态设置,在初始化组件时就进行静态上下文的覆盖(这个覆盖是将远spring容器的上下文对象引用加到我们预定设置)
* 不使用静态方式 可以使用@Service 方式进行注入
*
* @Resource
* private ApplicationContext applicationContext;
* 可实现同等效果 且不用实现任何类 该继承方法适合注入ApplicationContext 为静态类的时候
*
*/
private static ApplicationContext applicationContext = null;
@PostMapping("/getSalary")
public ResultObject getSalary(@RequestBody SalaryParams salaryParams){
ResultObject instance = ResultObject.createInstance(true);
AbstractManageAllowanceCalService service = null;
if ("ZG".equals(salaryParams.getType())){
// 主管
service = (SupervisorSalaryCalculationService)applicationContext.getBean("supervisorSalaryCalculationService");
}else if ("YG".equals(salaryParams.getType())){
service = (WorkerSalaryCalculationService) applicationContext.getBean("workerSalaryCalculationService");
}
if (Objects.nonNull(service)){
BigDecimal amount = service.calAmount(salaryParams.getAmount());
instance.setData(amount);
instance.setSuccess(true);
instance.setMessage("计算成功!");
return instance;
}
instance.setCode(500);
instance.setSuccess(false);
instance.setMessage("未匹配到对应计算方法!");
return instance;
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
SalaryController.applicationContext = applicationContext;
}
}
测试结果