如何优雅地使用设计模式【3】——策略模式

发布于 2022-06-03 22:14

一、策略模式


策略模式(英文 Strategy Pattern),将一个类里面的不同条件下的行为或者算法封装起来,形成独立的、可相互替代的策略对象,在运行时,根据参数可以获取不同的策略对象(可以配合工厂模式使用),从而可以产生不同的执行效果。充分体现中多用组合,少用继承针对接口编程等面向对象设计原则。


二、优缺点


  • 优点

    • 行为或者处理算法可以自由替换

    • 避免在代码里面使用多重判断

    • 方便后期拓展,新增策略只要实现接口即可

  • 缺点

    • 策略类要对外暴露

    • 客户端要使用就必须理解这些不同的策略,才能选择适当的策略


三、示例代码


1、新建一个策略类的接口,里面定义里两个方法,一个是用来处理不同行为的方法,一个是行为处理前调用的方法(也可以不用,随缘),可以用来处理一些其他操作,分割、过滤、排序参数,等等。

package com.test.strategyPattern.service;
import java.util.List;
public interface Strategy { /** * 对入参的处理,根据实际需要返回需要的数据结构 * * @param param param * @return 返回处理集合 */ List<String> preHandler(String param);
/** * 具体处理函数 * * @param params params */ void process(List<String> params);}


2、实现该接口的具体策略类,这里定义了两个类

package com.test.strategyPattern.service.impl;
import com.alibaba.fastjson.JSON;import com.test.strategyPattern.service.Strategy;import org.apache.commons.lang3.StringUtils;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.stereotype.Component;
import java.util.Arrays;import java.util.List;import java.util.stream.Collectors;
@Component(value = "defaultStrategy")public class DefaultStrategy implements Strategy { private static final Logger LOGGER = LoggerFactory.getLogger(DefaultStrategy.class); @Override public List<String> preHandler(String param) { LOGGER.info("split param [{}] , then return array .", param); return Arrays.stream(param.split(",")).filter(StringUtils::isNotBlank).collect(Collectors.toList()); }
@Override public void process(List<String> params) { LOGGER.info("process param [{}] , begin to process .", JSON.toJSONString(params)); // handler continue params.forEach(System.out::println); }}
package com.test.strategyPattern.service.impl;
import com.alibaba.fastjson.JSON;import com.test.strategyPattern.service.Strategy;import org.apache.commons.lang3.StringUtils;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.stereotype.Component;
import java.util.Arrays;import java.util.List;import java.util.stream.Collectors;
@Component(value = "secondStrategy")public class SecondStrategy implements Strategy { private static final Logger LOGGER = LoggerFactory.getLogger(SecondStrategy.class); @Override public List<String> preHandler(String param) { LOGGER.info("split param [{}] , then return array .", param); return Arrays.stream(param.split(",")).filter(StringUtils::isNotBlank).collect(Collectors.toList()); }
@Override public void process(List<String> params) { LOGGER.info("process param [{}] , begin to process .", JSON.toJSONString(params)); // handler continue params.forEach(str -> { System.out.println("[second]-" + str); }); }}


3、获取策略类的工厂类,通过 @Autowired 注解,spring 可以自动将容器里面实现 Strategy 接口的策略类,加载到 map 里面,而且 key 是bean的id,value 是对应的策略实现类

package com.test.strategyPattern.service;
import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Component;
import java.util.Map;
@Componentpublic class StrategyFactory { private static final Logger LOGGER = LoggerFactory.getLogger(StrategyFactory.class);
// 自动获取实现Strategy接口的策略类 // key:bean的id,value:对应的策略实现类 @Autowired private Map<String, Strategy> strategyMap = new ConcurrentHashMap<>();
public Strategy getStrategyByName(String name) { Strategy strategy = strategyMap.get(name); // commons-lang3 3.9 版本 if (ObjectUtils.isEmpty(strategy)) { throw new RuntimeException("Strategy is null."); } return strategy; }}


4、简单写一个接口测试一下功能

package com.test.strategyPattern.controller;
import com.alibaba.fastjson.JSONObject;import com.test.strategyPattern.service.Strategy;import com.test.strategyPattern.service.StrategyFactory;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController@RequestMapping("/test")public class TestController { private static final Logger LOGGER = LoggerFactory.getLogger(TestController.class);
@Autowired private StrategyFactory strategyFactory;
@GetMapping("/strategy") public void testStrategy(@RequestParam String name, @RequestParam String param) { Strategy strategyByName = strategyFactory.getStrategyByName(name); List<String> params = strategyByName.getParams(param); strategyByName.process(params); }}


5、结果展示
当浏览器访问

http://localhost:8080/test/strategy?name=defaultStrategy&param=zhangsan,lisi , 后台可以看到


当浏览器访问

http://localhost:8080/test/strategy?name=secondStrategy&param=zhangsan,lisi , 后台可以看到


6、如果页面传的参数不能对应,其实可以通过别名配置文件来做映射
1)新增配置类

package com.test.strategyPattern.common;
import org.springframework.boot.context.properties.ConfigurationProperties;import org.springframework.context.annotation.PropertySource;import org.springframework.stereotype.Component;
import java.util.HashMap;import java.util.Map;
@Component@PropertySource("classpath:StrategyPatternResources/application-strategy.properties")@ConfigurationProperties(prefix = "strategy.pattern")public class AliasConfiguration {
// aliasMap 要对应到配置文件里面key的最后一个值 private Map<String, String> aliasMap = new HashMap<>();
public static final String DAFAULT_ALIAS_NAME = "defaultStrategy";
public Map<String, String> getAliasMap() { return aliasMap; }
public void setAliasMap(Map<String, String> aliasMap) { this.aliasMap = aliasMap; }
public String getStrategyByAlias(String alias) { return aliasMap.get(alias); }}


2)在 resources 下面新增文件夹 StrategyPatternResources ,新增配置文件 application-strategy.properties

strategy.pattern.aliasMap.zhangsan=defaultStrategystrategy.pattern.aliasMap.lisi=secondStrategy

3) 修改 StrategyFactroy 类,在获取策略类前,先去别名配置表转化一下

package com.test.strategyPattern.service;
import com.test.strategyPattern.common.AliasConfiguration;import org.apache.commons.lang3.ObjectUtils;import org.apache.commons.lang3.StringUtils;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Component;
import java.util.Map;import java.util.concurrent.ConcurrentHashMap;
@Componentpublic class StrategyFactory { private static final Logger LOGGER = LoggerFactory.getLogger(StrategyFactory.class);
@Autowired private AliasConfiguration aliasConfiguration;
// 自动获取实现Strategy接口的策略类 // key:bean的id,value:对应的策略实现类 @Autowired private Map<String, Strategy> strategyMap = new ConcurrentHashMap<>();
public Strategy getStrategyByName(String name) { String strategyByAlias = aliasConfiguration.getStrategyByAlias(name); if (StringUtils.isBlank(strategyByAlias)) { LOGGER.warn("when name is [{}] , find not match strategy , return default.", name); return strategyMap.get(AliasConfiguration.DAFAULT_ALIAS_NAME); }
Strategy strategy = strategyMap.get(strategyByAlias); if (ObjectUtils.isEmpty(strategy)) { throw new RuntimeException("Strategy is null."); } return strategy; }}


7、验证功能
当浏览器访问

http://localhost:8080/test/strategy?name=zhangsan&param=zhangsan,lisi , 后台可以看到



当浏览器访问

http://localhost:8080/test/strategy?name=lisi&param=zhangsan,lisi , 后台可以看到


本文来自网络或网友投稿,如有侵犯您的权益,请发邮件至:aisoutu@outlook.com 我们将第一时间删除。

相关素材