如何优雅地使用设计模式【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;
@Component
public 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;
public class TestController {
private static final Logger LOGGER = LoggerFactory.getLogger(TestController.class);
private StrategyFactory strategyFactory;
public void testStrategy( String name, String param) {
Strategy strategyByName = strategyFactory.getStrategyByName(name);
List<String> params = strategyByName.getParams(param);
strategyByName.process(params);
}
}
5、结果展示
当浏览器访问
http://localhost:8080/test/strategy?name=defaultStrategy¶m=zhangsan,lisi
, 后台可以看到
当浏览器访问
http://localhost:8080/test/strategy?name=secondStrategy¶m=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;
"classpath:StrategyPatternResources/application-strategy.properties") (
"strategy.pattern") (prefix =
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=defaultStrategy
strategy.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;
@Component
public 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¶m=zhangsan,lisi
, 后台可以看到
当浏览器访问
http://localhost:8080/test/strategy?name=lisi¶m=zhangsan,lisi
, 后台可以看到
本文来自网络或网友投稿,如有侵犯您的权益,请发邮件至:aisoutu@outlook.com 我们将第一时间删除。
相关素材