摘要:
原著:程杰。本文是在原文学习的基础上对原文的demo进行Java的翻译,是根据自己的理解实现的,欢迎各位交流感受。
主要案例,策略模式实现商场收银。假如说这是 一道面试题,如果让我去做的话,说实话,我不会想到会用设计者模式去做,因为感觉只要是实现了最终的目的就可以了,干嘛非要弄得这么麻烦。但其实做完之后会发现差别其实是挺大的。平时在写业务代码的时候也是一样,不单单是需要将当前的业务实现了就可以,还要考虑代码的可读性和后期的可维护性,要不然估计就算想离职老板都不会让走吧,哈哈。
demo介绍:实现一个商场收银功能的计算器,该计算器可以实现商品的打折,满减等优惠。
在实现上,通过一个上下文类实现对简单工厂类的封装,对程序中的各个类进行解耦合,降低各类之间的关联性,好处就是,当有一天需求变化的时候,可以尽可能少的去修改代码。废话不多说,上代码:
/**
* 收费父类
*
* @author weiming.liu
* @Date 2019年5月11日 下午8:41:35
*/
public abstract class CashSuper {
/**
* 计算金额
*
* @param money
* @return
*/
public abstract String acceptCash(BigDecimal money);
}
/**
* 正常收费
*
* @author weiming.liu
* @Date 2019年5月11日 下午9:18:52
*/
public class CashNormal extends CashSuper {
@Override
public String acceptCash(BigDecimal money) {
return money.toString();
}
}
/**
* 打折收费
*
* @author weiming.liu
* @Date 2019年5月11日 下午8:46:49
*/
public class CashRebate extends CashSuper {
private int moneyRebate = 10; // 默认为不打折
public CashRebate(int moneyRebate) {
if (moneyRebate > 10 || moneyRebate <= 0) {
moneyRebate = 10;
}
this.moneyRebate = moneyRebate;
}
@Override
public String acceptCash(BigDecimal money) {
return money.multiply(new BigDecimal(moneyRebate).divide(new BigDecimal(10))).toString();
}
}
/**
* 满减计算
*
* @author weiming.liu
* @Date 2019年5月11日 下午9:22:43
*/
public class CashReturn extends CashSuper {
private BigDecimal moneyCondition = new BigDecimal(0);
private BigDecimal moneyReturn = new BigDecimal(0);
public CashReturn(double moneyCondition, double moneyReturn) {
if (moneyCondition < moneyReturn) {
System.out.println("你傻啊!");
}
this.moneyCondition = new BigDecimal(moneyCondition);
this.moneyReturn = new BigDecimal(moneyReturn);
}
@Override
public String acceptCash(BigDecimal money) {
BigDecimal result = money;
if (moneyCondition.compareTo(money) < 1) { // 如果满减金额小于小于等于付款金额
result = money.subtract(money.divide(moneyCondition,0, BigDecimal.ROUND_DOWN).multiply(moneyReturn));
}
return result.toString();
}
}
/**
* 费用计算工厂类
* @author weiming.liu
* @Date 2019年5月11日 下午9:13:27
*/
public class CashFactory {
public static CashSuper createCashAccept(String type){
CashSuper cs = null;
switch (type) {
case "正常收费":
cs = new CashNormal();
break;
case "满300减100":
cs = new CashReturn(300, 100);
break;
case "打八折":
cs = new CashRebate(8);
break;
default:
cs = new CashNormal();
break;
}
return cs;
}
}
/**
* 上下文关联
*
* @author weiming.liu
* @Date 2019年5月11日 下午9:23:20
*/
public class CashContext {
private String type = "";
public CashContext(String type) {
this.type = type;
}
public String getResult(BigDecimal money){
CashSuper cs = CashFactory.createCashAccept(type);
return cs.acceptCash(money);
}
}
/**
* 金额计算客户端
*
* @author weiming.liu
* @Date 2019年5月11日 下午9:33:49
*/
public class CashClient {
public static void main(String[] args) {
CashContext csuper = new CashContext("正常收费");
String total = csuper.getResult(new BigDecimal(1000));
System.out.println(total);
}
}
在上述实现中,定义计算父类CashSuper.java,对于原价、打折和满减都继承该父类,并通过工厂类CashFactory.java去实例化计算的实现类,最后通过在客户端程序中引用上下文类CashContext.java去创建具体的计算类去实现折扣的计算。
在实现上,使用了Bigdecimal类替换原有的doulbl类,因为double类在计算的时候容易导致精度错乱问题,所以在在实现上采用了Bigdecimal类。
看完上述代码,你肯定也会发现,在代码中只有打八折,那么如果打九折或者打半折如何处理呢?难道把所有的折扣都维护一遍,然后用switch判断一遍?对于这个问题,如果各位有实现的方法可以留言交流。
书中摘抄:
1.策略模式是一种定义一系列算法的方法,从概念上来看,所有这些算法完成的都是相同的工作,只是实现方式不同,它可以以相同的方式调用所有的算法,减少了各种算法类与使用算法类之间的耦合;
2.策略模式就是用来封装方法的,但在实践中,我们发现可以用它来封装几乎任何类型的规则,只要在分析过程中听到需要在不同时间应用不同的业务规则,就可以考虑使用策略模式处理这种变化的可能性。