根据不同的输入差异,应用不同的规则,得出不同的输出值
这种情况就适合策略模式。

需求:
计算一个订单运费的适合,会根据重量和目的地有各种计费规则:固定运费,满XX包邮,某几个省份包邮,超过多少重量加价多少等等。

如果选择了固定运费:让你设置一个固定运费是多少元,固定运费20元。

{“fixed_freigt”: 20}

比如说,选择了满X元包邮:让你设置一个满多少元可以包邮,没达到这么多钱的话,固定运费就是多少钱。

{“threshold”: 100, “less_than_threshold_freight”: 10}

比如说,选择了自定义规则之后:就是下面会出来多个UI组件,你可以加多个规则,每个规则:加一个规则,选择发货到哪些哪些省份,多少重量以内是多少钱;每增加多少g,增加多少钱。

[
{
“provinces”: “山西,陕西,甘肃,辽宁,黑龙江,吉林”,
“threshold”: “100g”,
“threshold_freight”: 10,
“incr_step”: “2g”,
“incr_freight”: 2
},
{
“provinces”: “云南,广西,湖南,江西”,
“threshold”: “100g”,
“threshold_freight”: 5,
“incr_step”: “1g”,
“incr_freight”: 1
}
]

可以调整和修改运费模板的名称、类型、规则、备注,
准备提交订单的确认页面上,就会根据每个商品的运费模板,计算出来每个商品对应的一个运费,
订单中心会将订单的数据传送过来,然后这里会根据每个商品绑定的运费模板,计算出来每个商品对应的一个运费。包括一个订单的总运费。

看到这种场景,直接就上策略模式,如果你不用策略模式的话,可能代码会写成什么样子呢?

if (type == 1) {
// 200行代码
} else if(type == 2) {
// 300行代码
} else if(type == 3) {
// 1000行代码
}

策略模式实现
首先定义一个运费计算器的接口
输入运费模板FreightTemplateDTO和订单
返回运费数值。

/**
 * 运费计算器接口
 * @author 
 *
 */
public interface FreightCalculator {

	/**
	 * 计算订单条目的运费
	 * @param freightTemplate 运费模板
	 * @param order 订单
	 * @param orderItem 订单条目
	 * @return 运费
	 * @throws Exception
	 */
	Double calculate(FreightTemplateDTO freightTemplate, 
			OrderInfoDTO order, OrderItemDTO orderItem) throws Exception;
	
}

四种策略对应四种实现:
FixedFreightCalculator- 固定运费计算器
ReachFreeFreightCalculator-满减策略
CustomRuleFreightCalculator-自定义规则计算器
DefaultFreightCalculator-默认运费计算器

/**
 * 固定运费计算器
 * @author
 *
 */
@Component
public class FixedFreightCalculator implements FreightCalculator {

	/**
	 * 计算订单条目的运费
	 * {“threshold”: 100, “less_than_threshold_freight”: 10}
	 * @param freightTemplate 运费模板
	 * @param order 订单
	 * @param orderItem 订单条目
	 * @return 运费
	 * @throws Exception
	 */
	public Double calculate(FreightTemplateDTO freightTemplate, 
			OrderInfoDTO order, OrderItemDTO orderItem) throws Exception {
		JSONObject rule = JSONObject.parseObject(freightTemplate.getRule());
		return rule.getDouble("fixed_freight");  
	}
	
}
/**
 * 满X元包邮运费计算器
 * @author
 *
 */
@Component
public class ReachFreeFreightCalculator implements FreightCalculator {

	/**
	 * 计算订单条目的运费
	 * {“threshold”: 100, “less_than_threshold_freight”: 10}
	 * 
	 * @param freightTemplate 运费模板
	 * @param order 订单
	 * @param orderItem 订单条目
	 * @return 运费
	 * @throws Exception
	 */
	public Double calculate(FreightTemplateDTO freightTemplate, 
			OrderInfoDTO order, OrderItemDTO orderItem) throws Exception {
           JSONObject rule = JSONObject.parseObject(freightTemplate.getRule());
           Double threshold = rule.getDouble("threshold");
           Double lessThanThresholdFreight = rule.getDouble("less_than_threshold_freight");
           Double amount = orderItem.getPurchaseQuantity() * orderItem.getPurchasePrice();
           if (amount >= threshold) {
           	  return 0.0;
		   } else {
           	 return lessThanThresholdFreight;
		   }
	}
	
}
/**
 * 自定义规则运费计算器
 * @author 
 *
 */
@Component
public class CustomRuleFreightCalculator implements FreightCalculator {

	/**
	 * 计算订单条目的运费
	 * [
	 * {
	 * “provinces”: “山西,陕西,甘肃,辽宁,黑龙江,吉林”,
	 * “threshold”: “100g”,
	 * “threshold_freight”: 10,
	 * “incr_step”: “2g”,
	 * “incr_freight”: 2
	 * },
	 * {
	 * “provinces”: “云南,广西,湖南,江西”,
	 * “threshold”: “100g”,
	 * “threshold_freight”: 5,
	 * “incr_step”: “1g”,
	 * “incr_freight”: 1
	 * }
	 * ]
	 * 
	 * @param freightTemplate 运费模板
	 * @param order 订单
	 * @param orderItem 订单条目
	 * @return 运费
	 * @throws Exception
	 */
	public Double calculate(FreightTemplateDTO freightTemplate, 
			OrderInfoDTO order, OrderItemDTO orderItem) throws Exception {
		String province = getProvinceFromAddress(order.getDeliveryAddress());
		Double totalGrossWeight = orderItem.getGoodsGrossWeight() * orderItem.getPurchaseQuantity();
		
		JSONArray rules = JSONArray.parseArray(freightTemplate.getRule());
		
		for(int i = 0; i < rules.size(); i++) {
			JSONObject rule = rules.getJSONObject(i);
			String provinces = rule.getString("provinces");
			
			if(!provinces.contains(province)) {
				continue;
			}
			
			Double threshold = rule.getDouble("threshold");
			Double thresholdFreight = rule.getDouble("threshold_freight");
			Double incrStep = rule.getDouble("incr_step"); 
			Double incrFreight = rule.getDouble("incr_freight"); 
			
			if(totalGrossWeight <= threshold) {
				return thresholdFreight;
			} else {
				return thresholdFreight + (totalGrossWeight - threshold) / incrStep * incrFreight;
			}
		}
		
		return 0.0;
	}
	
	/**
	 * 从地址中提取省份
	 * @param address 地址
	 * @return 省份
	 */
	private String getProvinceFromAddress(String address) {
		return address.substring(0, address.indexOf("省"));  
	}
	
}

将各种策略封装成工厂:

/**
 * 运费计算器工厂
 * @author 
 *
 */
@Component
public class FreightCalculatorFactory {

	/**
	 * 固定运费计算器
	 */
	@Autowired
	private FixedFreightCalculator fixedFreightCalculator;
	/**
	 * 满X元包邮运费计算器
	 */
	@Autowired
	private ReachFreeFreightCalculator reachFreeFreightCalculator;
	/**
	 * 自定义规则运费计算器
	 */
	@Autowired
	private CustomRuleFreightCalculator customRuleFreightCalculator;
	/**
	 * 默认运费计算器
	 */
	@Autowired
	private DefaultFreightCalculator defaultFreightCalculator;
	
	/**
	 * 获取运费模板对应的运费计算器
	 * @param freightTemplate 运费模板
	 * @return 运费计算器
	 */
	public FreightCalculator get(FreightTemplateDTO freightTemplate) {
		if(FreightTemplateType.FIXED.equals(freightTemplate.getType())) {
			return fixedFreightCalculator;
		} else if(FreightTemplateType.REACH_FREE.equals(freightTemplate.getType())) {
			return reachFreeFreightCalculator;
		} else if(FreightTemplateType.CUSTOM_RULE.equals(freightTemplate.getType())) {
			return customRuleFreightCalculator;
		}
		return defaultFreightCalculator;
	}
	
}

根据商品id,查询到对于的运费模板,然后获取运费计算器

/**
	 * 计算商品sku的运费
	 * @param goodsSkuDTO 商品sku DTO
	 * @return 商品sku的运费
	 */
	public Double calculateFreight(OrderInfoDTO order, OrderItemDTO orderItem) {
		try {
			// 获取商品对应的运费模板
			Long goodsId = orderItem.getGoodsId();
			GoodsDTO goods = commodityService.getGoodsById(goodsId);
			FreightTemplateDTO freightTemplate = freightTemplateService.getById(
					goods.getFreightTemplateId());
			
			// 获取运费计算器
			FreightCalculator freightCalculator = freightCalculatorFactory.get(freightTemplate);
			Double freight = freightCalculator.calculate(freightTemplate, order, orderItem);
			
			return freight;
		} catch (Exception e) {
			logger.error("error", e); 
			return 0.0;
		}
	}