一、介绍
在日常开发过程中,可能会遇到实现某一个功能有多种算法或者策略,我们可以根据环境或者条件的不同选择不同的算法或者策略来完成该功能。如查找、排序等,一种常用的方法是硬编码在一个类中,如需要提供多种查找算法,可以将这些算法写到一个类中,在该类中提供多个方法,每一个方法对应一个具体的查找算法;当然也可以将这些查找算法封装在一个统一的方法中,通过if…else…或者switch/case等条件判断语句来进行选择。这两种实现方法我们都可以称之为硬编码,如果需要增加一种新的查找算法,需要修改封装算法类的源代码;更换查找算法,也需要修改客户端调用代码。在这个算法类中封装了大量查找算法,该类代码将较复杂,维护较为困难。如果我们将这些策略包含在客户端,这种做法更不可取,将导致客户端程序庞大而且难以维护,如果存在大量可供选择的算法时问题将变得更加严重。以上的类似场景,我们都可以采用策略模式来优化代码。
二、案例
假设在某个电商平台中,有一个针对不同级别的用户购买商品时,对价格和邮费做不同折扣优惠的业务功能,不同用户级别我们可以当作不同的几个策略,假设现在有新用户、老用户、VIP用户、MVP用户四种用户群体,案例代码如下:
- 策略枚举
/**
* 优惠策略名称枚举
*
* @author zhangxs
**/
public enum Preferential {
/**
* 新客户优惠
*/
NEW_CUSTOM,
/**
* 老客户优惠
*/
OLD_CUSTOM,
/**
* VIP优惠
*/
VIP_CUSTOM,
/**
* MVP优惠
*/
MVP_CUSTOM,
}
- 策略接口
import strategy.constant.Preferential;
import javax.annotation.PostConstruct;
import java.math.BigDecimal;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* 优惠策略接口
*
* @author zhangxs
**/
public interface PreferentialService {
Map<Preferential,PreferentialService> STRATEGY_MAP = new ConcurrentHashMap<>();
/**
* 价格优惠
* @param price
* @return
*/
BigDecimal priceDiscount(BigDecimal price);
/**
* 邮费优惠
* @param postage
* @return
*/
BigDecimal postageDiscount(BigDecimal postage);
/**
* 获取优惠策略名称
* @return
*/
Preferential fetchPreferentialStrategy();
/**
* 接口的默认实现类 JDK1.8及以上版本适用
*/
@PostConstruct
default void init(){
STRATEGY_MAP.put(fetchPreferentialStrategy(),this);
}
}
- 策略上下文
import java.math.BigDecimal;
/**
* 优惠政策上下文
*
* @author zhangxs
**/
public class PreferentialContext {
private final PreferentialService preferentialService;
public PreferentialContext(PreferentialService preferentialService) {
this.preferentialService = preferentialService;
}
public BigDecimal preferentialStrategy(BigDecimal price,BigDecimal postage){
// 价格优惠后
BigDecimal priceDiscount = preferentialService.priceDiscount(price);
// 邮费优惠后
BigDecimal postageDiscount = preferentialService.postageDiscount(postage);
// 返回优惠后的价格
return priceDiscount.add(postageDiscount);
}
}
- 自定义针对新用户的优惠策略
import strategy.constant.Preferential;
import java.math.BigDecimal;
/**
* 新用户优惠策略实现
*
* @author zhangxs
**/
public class NewCustomPreferentialServiceImpl implements PreferentialService {
@Override
public BigDecimal priceDiscount(BigDecimal price) {
// 价格打95折
return price.multiply(new BigDecimal("0.95"));
}
@Override
public BigDecimal postageDiscount(BigDecimal postage) {
// 邮费打8折
return postage.multiply(new BigDecimal("0.8"));
}
@Override
public Preferential fetchPreferentialStrategy() {
return Preferential.NEW_CUSTOM;
}
}
- 自定义针对老用户的优惠策略
import strategy.constant.Preferential;
import java.math.BigDecimal;
/**
* 老用户优惠策略实现
*
* @author zhangxs
**/
public class OldCustomPreferentialServiceImpl implements PreferentialService {
@Override
public BigDecimal priceDiscount(BigDecimal price) {
return price.multiply(new BigDecimal("0.85"));
}
@Override
public BigDecimal postageDiscount(BigDecimal postage) {
return postage.multiply(new BigDecimal("0.6"));
}
@Override
public Preferential fetchPreferentialStrategy() {
return Preferential.OLD_CUSTOM;
}
}
- 自定义针对Vip用户的优惠策略
import strategy.constant.Preferential;
import java.math.BigDecimal;
/**
* VIP用户优惠策略实现
*
* @author zhangxs
**/
public class VipCustomPreferentialServiceImpl implements PreferentialService {
@Override
public BigDecimal priceDiscount(BigDecimal price) {
return price.multiply(new BigDecimal(0.75));
}
@Override
public BigDecimal postageDiscount(BigDecimal postage) {
return postage.multiply(new BigDecimal(0.4));
}
@Override
public Preferential fetchPreferentialStrategy() {
return Preferential.VIP_CUSTOM;
}
}
- 自定义针对Mvp用户的优惠策略
import strategy.constant.Preferential;
import java.math.BigDecimal;
/**
* MVP用户优惠策略实现
*
* @author zhangxs
**/
public class MvpCustomPreferentialServiceImpl implements PreferentialService {
@Override
public BigDecimal priceDiscount(BigDecimal price) {
return price.multiply(new BigDecimal("0.65"));
}
@Override
public BigDecimal postageDiscount(BigDecimal postage) {
return postage.multiply(new BigDecimal("0.2"));
}
@Override
public Preferential fetchPreferentialStrategy() {
return Preferential.MVP_CUSTOM;
}
}
- 测试验证
import strategy.service.MvpCustomPreferentialServiceImpl;
import strategy.service.PreferentialContext;
import java.math.BigDecimal;
/**
* 测试策略模式
*
* @author zhangxs
**/
public class PreferentialCalculate {
public static void main(String[] args) {
PreferentialContext preferentialContext = new PreferentialContext(new MvpCustomPreferentialServiceImpl());
BigDecimal result = preferentialContext.preferentialStrategy(new BigDecimal(7100), new BigDecimal(20));
System.out.println(result.setScale(2,BigDecimal.ROUND_HALF_UP));
}
}
可以根据实际业务场景,自定义策略,并在new PreferentialContext(new MvpCustomPreferentialServiceImpl())
时传入自定义的策略对象作为上下文构造器参数。
三、总结
个人认为策略模式是非常重要,且非常实用的一个设计模式,JDK中的线程池部分用到了策略模式,Spring中配置文件加载和注解扫描ApplicationContext中也用到了策略模式,当然还有很多框架中也都涉及到了,建议以后遇到类似的业务场景,需要优化代码时,可以通过策略模式优化。