1.什么是策略模式
策略这个词应该怎么理解,打个比方说,我们出门的时候会选择不同的出行方式,比如骑自行车、坐公交、坐火车、坐飞机、坐火箭等等,这些出行方式,每一种都是一个策略。 再比如我们去逛商场,商场现在正在搞活动,有打折的、有满减的、有返利的等等,其实不管商场如何进行促销,说到底都是一些算法,这些算法本身只是一种策略,并且这些算法是随时都可能互相替换的,比如针对同一件商品,今天打八折、明天满100减30,这些策略间是可以互换的。 策略模式(Strategy),定义了一组算法,将每个算法都封装起来,并且使它们之间可以互换。
2.使用场景
1、业务场景 现在需要接收来自某系统的回调,消息回调会有很多类型,比如私聊文本消息、私聊图片消息、新好友申请、私聊语音消息等,每一种消息类型,对应不同的逻辑处理,我们最开始是想直接if-else或者switch,走不同的分支逻辑搞定,但是这样扩展性不好,不易维护,这时我们想到了策略模式。 2、定义枚举类 枚举类存放各种回调类型,值代表的是具体实现类的类名,首字母这里小写。通过key就能得到对应的类beaName。
@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;}}
3、定义抽象类 抽象类定义抽象方法handler,每一种逻辑处理类只需要继承这个抽象类就可以了。
public abstract class AbstractMsgCallBackHandler {public final Boolean process(JSONObject json) {return this.handler(json);}/*** 处理相应逻辑** @param json* @return*/protected abstract Boolean handler(JSONObject json);
}
4、定义实现类 实现类具体处理业务逻辑,继承抽象类
@Component
@Slf4j
public class NewFriendMsgCallBackHandler extends AbstractMsgCallBackHandler {&#64;Resourceprivate CommonService commonService;&#64;Overrideprotected Boolean handler(JSONObject json) {JSONObject data &#61; json.getJSONObject("data");String wId &#61; data.getString("wId");String v1 &#61; data.getString("v1");String v2 &#61; data.getString("v2");Integer type &#61; data.getInteger("scene");Map param &#61; new HashMap<>();param.put("wId", wId);param.put("v1", v1);param.put("v2", v2);param.put("type", type);commonService.commonSendPost(param, UrlConstant.WK_ACCEPTUSER_URL);return Boolean.TRUE;}
}
5、调用抽象方法 第一步&#xff0c;先把抽象类注入&#xff0c;这里以map的方式注入&#xff0c;key为beanName&#xff0c;值为具体的实现类。
&#64;Resourceprivate Map abstractMsgCallBackHandlerMap;
第二步、通过类型获取beanName&#xff0c;从枚举中获取value
String beanName &#61; MsgCallBackEnum.getBeanName(messageType);
第三步、通过beanName获取抽象类
AbstractMsgCallBackHandler abstractMsgCallBackHandler &#61; abstractMsgCallBackHandlerMap.get(beanName);
第四步、调用抽象类抽象方法&#xff0c;会自动指向实现类
abstractMsgCallBackHandler.process(json);
整体的代码大概是这样的&#xff1a;
&#64;Slf4j
&#64;RestController
&#64;RequestMapping("/callBack")
public class CallBackController {&#64;Resourceprivate WxAdminService wxAdminService;&#64;Resourceprivate Map abstractMsgCallBackHandlerMap;/*** 消息接收服务地址** &#64;param msg* &#64;return*/&#64;TokenNeedless&#64;PostMapping("/getMsgCallBack")public Result getMsgCallBack(&#64;RequestBody String msg) {JSONObject json &#61; JSONObject.parseObject(msg);//消息类型Integer messageType &#61; json.getInteger("messageType");log.info("getMsgCallBack:{}", msg);String beanName &#61; MsgCallBackEnum.getBeanName(messageType);AbstractMsgCallBackHandler abstractMsgCallBackHandler &#61; abstractMsgCallBackHandlerMap.get(beanName);if (abstractMsgCallBackHandler &#61;&#61; null) {return Result.ok();}Boolean process &#61; abstractMsgCallBackHandler.process(json);log.info("getMsgCallBack_process:{}", process);return Result.ok();}
}
这样以后有新的类型&#xff0c;直接写一个实现抽象类的类就可以了&#xff0c;代码藕和度下降很多。
四、总结
- 何时使用 一个系统有许多类&#xff0c;而区分它们的只是他们直接的行为时
- 方法 将这些算法封装成一个一个的类&#xff0c;任意的替换
- 优点 算法可以自由切换 避免使用多重条件判断&#xff08;如果不用策略模式我们可能会使用多重条件语句&#xff0c;不利于维护&#xff09; 扩展性良好&#xff0c;增加一个策略只需实现接口即可
- 缺点 策略类数量会增多&#xff0c;每个策略都是一个类&#xff0c;复用的可能性很小 所有的策略类都需要对外暴露
- 使用场景 多个类只有算法或行为上稍有不同的场景 算法需要自由切换的场景 需要屏蔽算法规则的场景