一、什么是策略模式
官方定义:
策略模式主要是针对一组算法,将每个算法封装到具有共同接口的独立类中,使得他们可以互相替换.
个人理解:
策略模式就是将原先的if…else判断给封装起来了,使得整体系统更为流畅以及更加容易扩展.
二、为什么要使用策略模式
先看 [代码示例-01] 实现发送短信验证码的需求
这是一个真实的代码案例,从案例中可以看出,发送短信验证码需要根据不同的场景去做对应的业务处理(其实就是各种判断),在初期开发的时候使用if…else分支是比较合理的,因为开始时只有几个场景需要发送短信,但是随着需求的迭代,后期一直在新增场景,导致需要一直增加if分支,最终使得代码比较混乱并且不容易维护.
下面使用[策略模式+简单工厂模式]去优化这部分代码,请看 [代码示例-02]
从优化过后的案例中可以看出,咱们使用策略模式将原来的一堆if…else给封装起来了,这样的话后面无论怎么新增场景,咱们只需要新增对应的策略实现类,然后将策略实现类保存至Map集合就好了,调用方的代码完全不需要变动.
这就是使用策略模式的好处,可以有效的减少代码中的if…else分支代码,能使代码更加的简洁易维护,并且从代码结构上来说更加清晰明了.
三、代码示例
1、代码示例-01
//请求类
public class SendSmsCodeForm extends CommonForm {
@NotEmpty(message = "登录手机号不能为空")
@ApiModelProperty(value = "登录手机号")
private String phone;
@NotNull(message = "短信验证码使用场景不能为空")
@ApiModelProperty(value = "短信验证码使用场景")
private TempCredentialScene scene;
}
//验证码场景枚举类
@Getter
@AllArgsConstructor
public enum TempCredentialScene {
SMS_REG("SMS_REG", "短信注册验证码"),
SMS_LOGIN("SMS_LOGIN", "短信登录验证码");
//.........实际上还有很多场景
private String code;
private String name;
}
//具体业务类
public class ClassServiceImpl{
//发送短信验证码
public Result sendSmsCode(SendSmsCodeForm form) {
//.....业务前置逻辑
if (TempCredentialScene.SMS_LOGIN == form.getScene()) {
//...业务处理
}else if(TempCredentialScene.SMS_REG == form.getScene()){
//...业务处理
}else if(TempCredentialScene.SMS_UPDATE_PHONE== form.getScene()){
//...业务处理
}else{
//.....实际上还有很多 if分支
}
//......后续业务逻辑
return Result.success();
}
}
2、代码示例-02
//策略接口
public interface ISendSmsCodeVerifyStrategy {
//发送验证码校验
void sendSmsCodeVerify(SendSmsCodeForm form);
}
//具体策略实现-01
@Slf4j
@Service
public class SendSmsCodeRegisterVerify implements ISendSmsCodeVerifyStrategy {
@Override
public void sendSmsCodeVerify(SendSmsCodeForm form) {
log.info("短信注册验证码验证逻辑...");
}
}
//具体策略实现-02
@Service
@Slf4j
public class SendSmsCodeLoginVerify implements ISendSmsCodeVerifyStrategy {
@Override
public void sendSmsCodeVerify(SendSmsCodeForm form) {
log.info("短信登录验证码验证逻辑...");
}
}
//策略工厂-简单工厂模式(工厂使用单例)
public class SendSmsCodeVerifyStrategyFactory {
private SendSmsCodeVerifyStrategyFactory() {
init();
}
//使用静态内部类实现单例模式
private static class Singleton {
private static final SendSmsCodeVerifyStrategyFactory FACTORY = new SendSmsCodeVerifyStrategyFactory();
}
public static SendSmsCodeVerifyStrategyFactory getInstance() {
return Singleton.FACTORY;
}
//定义Map集合保存所有的策略实现类
private Map<TempCredentialScene, ISendSmsCodeVerifyStrategy> strategyMap;
/*
初始化方法,将策略实现类保存至Map集合
SpringBeanUtils类是从Spring容器中获取Bean,因为使用了@Service注解,不能直接使用new去实例化对象
*/
private void init() {
strategyMap = new HashMap<>(8);
SendSmsCodeRegisterVerify sendSmsCodeRegisterVerify = SpringBeanUtils.getBean(SendSmsCodeRegisterVerify.class);
strategyMap.put(TempCredentialScene.SMS_REG, sendSmsCodeRegisterVerify);
SendSmsCodeLoginVerify sendSmsCodeLoginVerify = SpringBeanUtils.getBean(SendSmsCodeLoginVerify.class);
strategyMap.put(TempCredentialScene.SMS_LOGIN, sendSmsCodeLoginVerify);
}
//根据场景获取具体的策略类
public ISendSmsCodeVerifyStrategy get(TempCredentialScene scene) {
ISendSmsCodeVerifyStrategy strategy = strategyMap.get(scene);
if (null == strategy) {
throw new RuntimeException("未知的策略...");
}
return strategy;
}
}
//业务Service
@Service
public class SysTempCredentialServiceImpl implements ISysTempCredentialService {
@Override
public Result sendVerifyCodeSms(SendSmsCodeForm form) {
//....前置逻辑处理
//获取策略类,调用策略方法
ISendSmsCodeVerifyStrategy sendSmsCodeVerifyStrategy = SendSmsCodeVerifyStrategyFactory.getInstance().get(form.getScene());
sendSmsCodeVerifyStrategy.sendSmsCodeVerify(form);
//....后置逻辑处理
return Result.success();
}
}
四丶在实际开发中的应用
1、实际案例-01
首先根据 [代码示例-02] 咱们已经将原先的if…else相关的代码做了对应的封装,也满足了策略模式的要求,后期扩展也比较容易,但是小伙伴们也能发现,在使用了策略模式的情况下,每次新增一个策略就需要新增一个策略实现类,这样的话在场景(TempCredentialScene)非常多的情况下,就需要新增很多实现类,但是每个实现类中可能就几个方法而已,这样明显是不合理的,因为像这种业务场景是很常见的,如果都去新增策略实现类的话就很容易导致类膨胀,反而使得代码更加复杂了.
所以针对这种并不算很大的if…else分支的情况下,(类似支付方式:支付宝,微信,银联 这种分支就算大分支,因为他的实现逻辑肯定很复杂,这种的肯定还是需要新增策略实现类的),可以使用JAVA8的Function函数来更进一步的优化代码
使用策略模式+简单工厂模式+JAVA8-Function函数去优化 [代码示例-02] (此模式已经在项目中正式使用)
//优化策略工厂类
import java.util.function.Function;
public class SendSmsCodeVerifyStrategyFactory {
private SendSmsCodeVerifyStrategyFactory() {
init();
}
private static class Singleton {
private static final SendSmsCodeVerifyStrategyFactory FACTORY = new SendSmsCodeVerifyStrategyFactory();
}
public static SendSmsCodeVerifyStrategyFactory getInstance() {
return Singleton.FACTORY;
}
/*
这里的Map的Value修改为保存Function函数
*/
private Map<String, Consumer<SendSmsCodeForm>> strategyMap;
private void init() {
strategyMap = new HashMap<>(8);
//获取SysTempCredentialServiceImpl中的方法,保存至Map集合中
SysTempCredentialServiceImpl service = SpringBeanUtils.getBean(SysTempCredentialServiceImpl.class);
strategyMap.put("SMS_REG", service::sendSmsCodeRegisterVerify);
strategyMap.put("SMS_LOGIN", service::sendSmsCodeLoginVerify);
}
//执行函数
public void accept(SendSmsCodeForm form) {
Consumer<SendSmsCodeForm> consumer = strategyMap.get(form.getScene());
if (null == consumer) {
throw new RuntimeException("未知的策略");
}
consumer.accept(form);
}
}
//业务Service
@Service
@Slf4j
public class SysTempCredentialServiceImpl implements ISysTempCredentialService {
@Override
public void sendVerifyCodeSms(SendSmsCodeForm form) {
SendSmsCodeVerifyStrategyFactory.getInstance().accept(form);
}
public void sendSmsCodeRegisterVerify(SendSmsCodeForm form) {
log.info("短信注册验证码验证逻辑...");
}
public void sendSmsCodeLoginVerify(SendSmsCodeForm form) {
log.info("短信登录验证码验证逻辑...");
}
}
从上述案例中可以看出,针对这种并不算很大的if…else分支,并不需要新增策略类,只需要新增对应的策略方法就好了(将原来的策略类变成策略方法),然后策略工厂由原来的保存策略类变成保存策略方法,最后根据Key去执行策略方法,
其实这样也算满足了开闭原则,因为后面新增场景的话只需要新增方法就好了,调用方的方法仍然不需要任何修改,这样既满足了减少代码中的if…else也解决了策略模式中类太多的问题.
五、总结
关于JAVA8的Function函数请同学自行百度
点击跳转百度文库中的JAVA-Function函数讲解
策略模式一般不会单独使用,通常是配合工厂模式使用,由一个变量(Key)去决定执行哪个具体的策略,合理的策略模式可以有效的减少代码中大量if…else,并且能够方便的扩展.
至此,策略模式就整理完毕辣~~