Java策略模式的基本使用
本文有引用两个网址:
Java策略模式基本使用
设计模式应用之策略模式
一、简介
1.定义
策略模式的用意是针对一组算法,将每一个算法封装到具有共同接口的独立类中,从而使得它们可以相互替换。策略模式使得算法可以在不影响到客户端的情况下发生变化。
策略这个词应该怎么理解,打个比方说,我们出门的时候会选择不同的出行方式,比如骑自行车、坐公交、坐火车、坐飞机、坐火箭等等,这些出行方式,每一种都是一个策略。 再比如我们去逛商场,商场现在正在搞活动,有打折的、有满减的、有返利的等等,其实不管商场如何进行促销,说到底都是一些算法,这些算法本身只是一种策略,并且这些算法是随时都可能互相替换的,比如针对同一件商品,今天打八折、明天满100减30,这些策略间是可以互换的。 策略模式(Strategy),定义了一组算法,将每个算法都封装起来,并且使它们之间可以互换。
二、应用场景
1、业务场景 现在需要接收来自某系统的回调,消息回调会有很多类型,比如私聊文本消息、私聊图片消息、新好友申请、私聊语音消息等,每一种消息类型,对应不同的逻辑处理,我们最开始是想直接if-else或者switch,走不同的分支逻辑搞定,但是这样扩展性不好,不易维护,这时我们想到了策略模式。
2、业务中一些用户需求变化的定制,比如 这个用户需要对广州的航班进行业务处理,另一个用户需要对厦航的航班进行特殊的处理(定制话,根据用户到需求来写对应的业务)。
3、 容错恢复机制是应用程序开发中非常常见的功能。那么什么是容错恢复呢?简单点说就是:程序运行的时候,正常情况下应该按照某种方式来做,如果按照某种方式来做发生错误的话,系统并不会崩溃,也不会就此不能继续向下运行了,而是有容忍出错的能力,不但能容忍程序运行出现错误,还提供出现错误后的备用方案,也就是恢复机制,来代替正常执行的功能,使程序继续向下运行。
举个实际点的例子吧,比如在一个系统中,所有对系统的操作都要有日志记录,而且这个日志还需要有管理界面,这种情况下通常会把日志记录在数据库里面,方便后续的管理,但是在记录日志到数据库的时候,可能会发生错误,比如暂时连不上数据库了,那就先记录在文件里面,然后在合适的时候把文件中的记录再转录到数据库中。
对于这样的功能的设计,就可以采用策略模式,把日志记录到数据库和日志记录到文件当作两种记录日志的策略,然后在运行期间根据需要进行动态的切换。
三、基础使用
1、定义一个出参结果类
用来返回出去
@Data
public class SendResult {
public SendResult(Boolean result, String message) {
this.result = result;
this.message = message;
}
// 状态
private Boolean result;
// 消息
private String message;
}
2、定义枚举类 – 根据业务需求可要可不要
@Getter
public enum MsgCallBackEnum {
//新好友申请
NEWFRIEND_MSG_CALL_BACK(0, "newFriendMsgCallBackHandler"),
//添加好友成功
ADDFRIENDSUCCESS_MSG_CALL_BACK(16, "addfriendSuccessMsgCallBackHandler"),
//私聊文本消息
PRIVATECHATTEXT_MSG_CALL_BACK(5, "privateChatTextMsgCallBackHandler"),
//私聊图片消息
PRIVATECHATIMAGE_MSG_CALL_BACK(6, "privateChatImageMsgCallBackHandler"),
//私聊视频消息
privateChatVIDEO_MSG_CALL_BACK(7, "privateChatVideoMsgCallBackHandler"),
//私聊语音消息
PRIVATECHATVOICE_MSG_CALL_BACK(8, "privateChatVoiceMsgCallBackHandler");
private Integer type;
private String beanName;
MsgCallBackEnum(Integer type, String beanName) {
this.type = type;
this.beanName = beanName;
}
public static String getBeanName(Integer type) throws BusinessException {
for (MsgCallBackEnum msgCallBackStrategyEnum : MsgCallBackEnum.values()) {
if (msgCallBackStrategyEnum.type.equals(type)) {
return msgCallBackStrategyEnum.getBeanName();
}
}
return null;
}
}
2、定义一个接口
统一定义一个处理需求的方法,一个用来判断是否支持的方法
public interface OpenApi {
/**
* 校验当前接口是否支持
*
* @param url 接口请求路径
* @return
*/
boolean support(String uri);
/**
* 对外执行方法
*
* @param url 接口请求路径
* @param result 返回结果
* @return
*/
SendResult handle(String url, Object[] result);
}
3、定义抽象类
抽象类定义抽象方法 handle,每一种逻辑处理类只需要继承这个抽象类就可以了。
public abstract class AbstractOpenApi implements OpenApi {
/**
* 对外执行方法
*
* @param uri ws请求地址
* @param result 返回结果
* @return
*/
@Override
public SendResult handle(String uri, Object[] result) {
return getResult(uri, result);
}
/**
* 自己实现方法
*
* @param uri
* @param result
* @return
*/
public abstract SendResult getResult(String uri, Object[] result);
}
4.定义实现类
实现类具体处理业务逻辑,继承抽象类
@RefreshScope
@Service
@Slf4j
public class ResultApi1 extends AbstractOpenApi {
/*@Value(value = "${wsResultApi1}")
private String URL;*/
private final static String URL = "http://127.0.0.1:8080";
/**
* 自己实现方法
*
* @param url
* @param result
* @return
*/
@Override
public SendResult getResult(String url, Object[] result) {
log.info("业务处理1类");
// JSON 转换
JSONObject jsonObject = (JSONObject) JSONObject.parse(result[0].toString());
// 获取 JSON 转换后的值并返回出去
return new SendResult(jsonObject.getBoolean("Statue"),
jsonObject.getString("Messages"));
}
/**
* 校验当前接口
*
* @param url
* @return
*/
@Override
public boolean support(String url) {
return URL.equals(url);
}
}
5、测试类
package com.xx.send.ws;
import com.xx.send.SendApp;
import com.xx.send.ws.handle.WsResultHandleMangers;
import lombok.extern.slf4j.Slf4j;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import javax.annotation.Resource;
/**
* ws接口处理测试类
*
* @Date 2022/3/14 20:34
* @Version 1.0
*/
@Slf4j
@RunWith(SpringRunner.class)
@SpringBootTest(classes = SendApp.class)
public class WsResultTest {
@Resource
private WsResultHandleMangers wsResultHandleMangers;
public static final String URL = "http://172.26.145.45:8081";
@Test
public void wsTest() {
String message1 = "{\"Statue\":false,\"Messages\":\"解析失败!\"}";
String message2 = "{\"Statue\":true,\"Messages\":\"解析成功!\"}";
Object[] messages = new String[]{message2};
wsResultHandleMangers.handle(URL, messages);
log.info("=====" + message2);
}
}
四、测试结果
结果:
五、总结
1.何时使用 一个系统有许多类,而区分它们的只是他们直接的行为时
2.方法 将这些算法封装成一个一个的类,任意的替换
3.优点 算法可以自由切换 避免使用多重条件判断(如果不用策略模式我们可能会使用多重条件语句,不利于维护) 扩展性良好,增加一个策略只需实现接口即可
4.缺点 策略类数量会增多,每个策略都是一个类,复用的可能性很小 所有的策略类都需要对外暴露
5.使用场景 多个类只有算法或行为上稍有不同的场景 算法需要自由切换的场景 需要屏蔽算法规则的场景