我们先来了解一下策略模式的概念
定义:
- 策略模式定义了一系列的算法,并将每一个算法封装起来,使每个算法可以相互替代,使算法本身和使用算法的客户端分割开来,相互独立
结构:
-
策略接口角色IStrategy:用来约束一系列具体的策略算法,策略上下文角色ConcreteStrategy使用此策略接口来调用具体的策略所实现的算法。
-
具体策略实现角色ConcreteStrategy:具体的策略实现,即具体的算法实现。
-
策略上下文角色StrategyContext:策略上下文,负责和具体的策略实现交互,通常策略上下文对象会持有一个真正的策略实现对象,策略上下文还可以让具体的策略实现从其中获取相关数据,回调策略上下文对象的方法。
我们再来分析一下平时的业务场景中,有什么场景可以用到我们的策略模式
首先举一个用烂了的例子某商城项目的会员,针对某件商品,不同会员等级有不同会员的处理方式,比如普通会员无折扣,青铜会员满100-10,白银会员打八折,黄金会员打八折且满100-20,需求几乎没有重合性只能依据类型判断.
第二个例子,众所周知每个提供翻译功能的平台基本都会有一些免费的额度,比如百度翻译,有道翻译等等如果我们想白嫖这些翻译的时间,就要根据不同家的文档去实现不同的翻译接口.
上面两个例子在普通的代码中基本就是
//伪代码
public void discount(int userType){
if(userType==黄金会员){
//do sth
}else if(userType==白银会员){
//do sth
}else if(...)
}
每加一个会员等级 都会对应一个if-else如果会员等级躲起来或者增加了新的折扣模式时都要来处理这个类,并且相应的要增加if-else,如果每个处理方式由不同的人开发,还会涉及到代码冲突问题.
常见的消除if-else的方法是使用工厂+策略模式,实际上也是用switch-case方式,代码量没有减少,反而有所增加,并且并没有结合实际开发框架,毕竟Java业务开发中Spring基本上是必不可少的,所以我针对Spring的bean管理功能对策略模式进行了自己的一个实现.
首先要创建一个处理策略的顶级接口,还是针对刚刚的那个业务场景创建一个会员处理顶级接口
public interface MemberDiscountService{
public void doDiscount();
}
接下来创建对应的处理类
//白银会员
@Service("silverMember")
public class SilverMemberDiscountServiceImpl implement MemberDiscountService{
@Override
public void doDiscount(){
//do sth
}
}
//黄金会员
@Service("glodMember")
public class GoldMemberDiscountServiceImpl implement MemberDiscountService{
@Override
public void doDiscount(){
//do sth
}
}
可以看到我们在注册实现类的时候根据不同的实现方式注册了不同名称的实现类
接下来我们创建一个绑定类型与实现结果的枚举
public enum MemberTypeEnum{
SILVER_MEMBER(1,"白银会员"),GOLD_MEMBER(2,"黄金会员");
int code;
String msg;
MemberTypeEnum(int code,String msg){
this.code= code;
this.msg=msg;
}
public static MemberTypeEnum getByCode(int code){
for(MemberTypeEnum memberType:MemberTypeEnum.values()){
if(memberType.code==code){
retur memberType;
}
}
return null;
}
}
接下来就是在项目中如何调用对应的实现方法了
首先我们要利用的Spring的上下文,也是项目中常用的SpringBeanUtil,贴一下代码
public class SpringBeanUtil implements ApplicationContextAware {
/**
* 直接通过Spring 上下文获取SpringBean,用于多线程环境
*/
// Spring应用上下文环境
private static ApplicationContext applicationContext;
/**
* 实现ApplicationContextAware接口的回调方法,设置上下文环境
*/
@Override
public void setApplicationContext(ApplicationContext applicationContext)
throws BeansException {
SpringBeanUtil.applicationContext = applicationContext;
}
public static ApplicationContext getApplicationContext() {
return applicationContext;
}
/**
* 获取对象 这里重写了bean方法,起主要作用
* example: getBean("userService")//注意: 类名首字母一定要小写!
*/
public static <T> T getBean(String beanId) throws BeansException {
return (T) applicationContext.getBean(beanId);
}
/**
* 获取对象 byType
* @param beanClass :
* @return T
* @throws
* @Date 2019/9/11
*/
public static <T> T getBean(Class<T> beanClass) throws BeansException {
return applicationContext.getBean(beanClass);
}
我们可以根据注册的名称来进行不同的Bean调用
public void memberDiscount(int memberType){
//通过memberType枚举去映射到对应的名称来获取实现类
MemberDiscountService memberDiscountService = SpringeBeanUtil.getBean(MemberTypeEnum.getByCode(memberType).getMsg());
memberDiscountService.doDiscount();
}
通过这种方式即可实现简单的策略调用,并且彻底脱离了if-else,添加了对应的实现后只需要添加对应的枚举即可.