教你如何使用策略模式

策略模式

在策略模式中一个类的行为或者其算法在运行是可以进行改变,这种的类型也可以叫做行为型模式。

基本结构

  • 策略对象类(表示行为)
  • 策略父类

优缺点

优点

  • 将多个行为进行封装,能够根据配置进行替换。
  • 能够省略较多的判断语句 if...else...,便于维护。
  • 实现一个公共接口。
  • 符合开发的开闭原则。

缺点

  • 策略类会很多,一个行为一个策略类。
  • 所有的策略类都需要对外暴漏。

场景

  • 在一个系统里面有许多类,它们之间的区别仅在于它们的行为,那么使用策略模式可以动态地让一个对象在许多行为中选择一种行为。

  • 一个系统需要动态地在几种算法中选择一种。

  • 如果一个对象有很多的行为,如果不用恰当的模式,这些行为就只好使用多重的条件选择语句来实现。

实例场景

今天和朋友要去公园玩儿,那么就要选择出行方式,那么创建一个出行的接口,创建自行车、公交、步行等行为类,并且都继承出行的接口,再创建我的选择类(Context)来选择出行方式。至此大体完成。

代码

出行方式接口

/**
 * @author Surpass
 * @Package com.hkrt.demo
 * @Description: 出行方式(工具)
 * @date 2021/1/15 16:16
 */
public interface Strategy {

    void goOutTool();
}

出行方式实现类

公交车

/**
 * @author Surpass
 * @Package com.hkrt.demo.strategy
 * @Description: 公交车
 * @date 2021/1/15 16:20
 */
public class BusWayStrategy implements Strategy{

    @Override
    public void goOutTool() {
        System.out.println("今天坐公交车出去玩儿");
    }
}

步行

/**
 * @author Surpass
 * @Package com.hkrt.demo.strategy
 * @Description: 步行方式
 * @date 2021/1/15 16:19
 */
public class WalkWayStrategy implements Strategy {

    @Override
    public void goOutTool() {
        System.out.println("今天要步行出去玩儿");
    }
}

自己开车

/**
 * @author Surpass
 * @Package com.hkrt.demo.strategy
 * @Description: 开车
 * @date 2021/1/15 16:21
 */
public class CarWayStrategy implements Strategy {

    @Override
    public void goOutTool() {
        System.out.println("今天自己开车出去玩儿");
    }
}

出行方式上下文

/**
 * @author Surpass
 * @Package com.hkrt.demo.strategy
 * @Description: 选择行为(上下文)
 * @date 2021/1/15 16:22
 */
public class StrategyContext {

    private Strategy strategy;

    public StrategyContext(Strategy strategy) {
        this.strategy = strategy;
    }

    public void goOut(){
        strategy.goOutTool();
    }
}

测试类

/**
 * @author Surpass
 * @Package com.hkrt.demo.strategy
 * @Description: 我和朋友准备出去玩儿
 * @date 2021/1/15 16:24
 */
public class PlayTest {
    public static void main(String[] args) {

        /**
         * 选择步行出去玩儿
         */
        StrategyContext walkWayStrategy = new StrategyContext(new WalkWayStrategy());
        walkWayStrategy.goOutTool();

        /**
         * 选择坐公交出去玩儿
         */
        StrategyContext busWayStrategy = new StrategyContext(new BusWayStrategy());
        busWayStrategy.goOutTool();

        /**
         * 选择自己开车出去玩儿
         */
        StrategyContext carWayStrategy = new StrategyContext(new CarWayStrategy());
        carWayStrategy.goOutTool();

    }
}

执行结果

Connected to the target VM, address: '127.0.0.1:11631', transport: 'socket'
今天要步行出去玩儿
今天坐公交车出去玩儿
今天自己开车出去玩儿
Disconnected from the target VM, address: '127.0.0.1:11631', transport: 'socket'

这样一个简单的策略就是出来了,如果出现新增的策略,只需要实现公共的出行方式接口,然后写自己的逻辑就OK了。

SpringBoot中应用

接了个需求,对接第三方接口进行数据的校验,每一个第三方都是一套规范,考虑到后续的可扩展性,和内部的统一,使用策略类进行实现。

  • 创建一个配置菜单对接入的第三方进行记录(标志 channelCode)。
  • 创建校验接口。
  • 创建第三方接口类实现校验接口,类中写校验逻辑代码。

在这里我去掉了Context上下文,直接在逻辑代码中注入校验接口,根据配置中的第三方数据的 channelCode 判断走哪个第三方接口( channelCode的值就是该第三方接口类在Spring中的beanName)。

校验接口

public interface BusinessAuthChannelService {

    /**
     *   企业信息要素验证
     * @param dto 校验数据
     * @return java.util.Map<java.lang.String , java.lang.Object>
     * @throws
     * @author Surpass
     * @date 2021/1/7 15:56
     */
    Map<String, Object> auth(AuthChannelDto dto);
}

第三方校验实现类(业务代码不便展示)

@Slf4j
@Service("qcc")
public class QccAuthChannelServiceImpl implements BusinessAuthChannelService {

    private Gson gson = new Gson();

    /**
     *  鉴权查询
     * @param dto
     * @return java.util.Map<java.lang.String , java.lang.Object>
     * @throws
     * @author Surpass
     * @date 2021/1/11 13:51
     */
    @Override
    public Map<String, Object> auth(AuthChannelDto dto) {
        Map<String, Object> resultMap = new HashMap<>();
        return resultMap;
    }
}

校验接口控制层

@Slf4j
@RestController
@RequestMapping("/businessAuthChannel")
public class BusinessAuthChannelController {

    @Autowired
    private BusinessAuthChannelConfigService businessAuthChannelConfigService;

    @Autowired
    private BusinessAuthChannelOrderService businessAuthChannelOrderService;

    private Gson gson = new Gson();

    /**
     *   企业信息要素验证
     * @param dto
     * @return java.util.Map<java.lang.String , java.lang.Object>
     * @throws
     * @author Surpass
     * @date 2021/1/7 15:38
     */
    @RequestMapping("/auth")
    public Map<String,Object> auth(@RequestBody AuthChannelDto dto){
        log.info("企业信息要素验证,要素鉴权参数:"+gson.toJson(dto));
        Map<String, Object> map = new HashMap<>(2);
        Map<String, Object> authResult = null;
        //校验参数
        boolean validateParameter = this.validateParameter(dto);
        if (!validateParameter){
            map.put("rspCode", "99");
            map.put("rspMsg", "参数校验失败");
            log.info("企业信息要素验证,返回数据:"+map);
            return map;
        }

        //查询鉴权通道
        BusinessAuthChannelConfig config = this.getAuthChannelConfig(dto);
        //判断能够鉴权的标识
        boolean flag = false;
        if (config != null){
            //是否存在该通道编码的调用service实现类(spring bean中存在通道编码的bean)
            boolean existBean = SpringUtil.getApplicationContext().containsBean(config.getChannelCode());
            if (existBean){
                flag = true;
            }
        }
        if (!flag){
            map.put("rspCode", "99");
            map.put("rspMsg", "找不到可用通道");
            log.info("企业信息要素验证,返回数据:"+map);
            this.saveBusinessAuthChannelOrder(dto, authResult, config);
            return map;
        }

        //调用鉴权通道
        BusinessAuthChannelService authChannelService =
                (BusinessAuthChannelService)SpringUtil.getBean(config.getChannelCode());
        authResult = authChannelService.auth(dto);

        //保存信息记录
        this.saveBusinessAuthChannelOrder(dto, authResult, config);
        Map<String, Object> resultMap = (Map<String, Object>)authResult.get("resultMap");
        log.info("企业信息要素验证,返回数据:"+resultMap);
        return resultMap;
    }

    /**
     *   鉴权要素参数校验
     * @param dto
     * @return boolean
     * @throws
     * @author Surpass
     * @date 2021/1/12 15:32
     */
    private boolean validateParameter(AuthChannelDto dto){
    }

    /**
     *   根据参数条件查询可用的鉴权通道
     * @param dto
     * @return com.p4.risk.entity.BusinessAuthChannelConfig
     * @throws
     * @author Surpass
     * @date 2021/1/12 15:32
     */
    private BusinessAuthChannelConfig getAuthChannelConfig(AuthChannelDto dto){
        //根据参数在配置表中查询第三方接口配置信息
    }

    /**
     *   保存操作记录
     * @param dto 企业信息鉴权信息
     * @param authResult 鉴权结果
     * @param config 企业鉴权通道配置
     * @return void
     * @throws
     * @author Surpass
     * @date 2021/1/12 15:33
     */
    private void saveBusinessAuthChannelOrder(AuthChannelDto dto, Map<String, Object> authResult,
                                              BusinessAuthChannelConfig config){
       
    }
}

解释一下:主要是 auth 这个方法时主要流程,根据参数查询出来的第三方接口配置字段的 channelCode ,使用SpringUtil在Spring中查找是否存在有该值的beanName,如果不存在,则直接保存操作记录后返回。如果存在则使用 SpringUtil.getBean(channelCode)获得该编码的bean,然后调用 authResult = authChannelService.auth(dto) 就进入到第三方校验实现类中进行校验,校验完毕后保存操作记录,流程结束。

尾言

代码不全,但注释应该足够了,如果对上述代码有疑问的烦请留言,如果各位喜欢这篇文章,劳烦各位伸出小手点个赞,收个藏,关个注。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,425评论 4 361
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,058评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,186评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,848评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,249评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,554评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,830评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,536评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,239评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,505评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,004评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,346评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,999评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,060评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,821评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,574评论 2 271
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,480评论 2 267

推荐阅读更多精彩内容