代码优化之改良策略模式
前言
你还在为写if-else吗?你还在为if-else复杂可读性低而烦恼吗? 策略模式又相对复杂,简单的逻辑使用策略又感觉杀鸡焉用宰牛刀。那我们来一起看看策略模式与之变种吧!
策略模式
定义:在策略模式(Strategy Pattern)中,一个类的行为或其算法可以在运行时更改。这种类型的设计模式属于行为型模式。在策略模式中,我们创建表示各种策略的对象和一个行为随着策略对象改变而改变的 context 对象。策略对象改变 context 对象的执行算法
策略模式类图(通用类图):
策略的接口和具体的实现应该很好理解:
策略的接口相当于我们上面所讲的ResultSetHandler接口(定义了策略的行为)。
具体的实现相当于我们上面所讲的BeanHandler实现(接口的具体实现)
具体的实现一般还会有几个,比如可能还有ListBeanHandler、MapBeanHandler等等
很多有人有疑问会问。为什么还有一个context上下文呢,不直接调用策略呢?
答:更符合面向对象,统一管理。如果客户端直接根据策略进行交互复杂度增加。类似于我们去商店买东西而不去各个厂家。增加复杂度。
废话不多说,举个?…………
策略模式举例
作为一个掘金新人如何登上热榜呢?我就有很多途径,好好写文档;请水军点赞涨粉…等等手段。
接口
/**
* 登热榜的具体措施
* @author
* @version Id: IncreaseFansStrategy.java, v 0.1 2021/5/13 8:11 下午 wuchaobo Exp $$
*/
public interface IncreaseFansStrategy {
void action();
}
复制代码
具体的策略实现(请水军、努力写文档等等策略)
/**
* @author
* @version Id: NavyIHandler.java, v 0.1 2021/5/13 8:16 下午 Exp $$
*/
public class NavyIHandler implements IncreaseFansStrategy {
@Override
public void action() {
System.out.println("请水军点赞涨粉");
}
}
/**
* 努力写文章
* @version Id: greatHandler.java, v 0.1 2021/5/13 8:18 下午 wuzhaobo Exp $$
*/
public class GreatHandler implements IncreaseFansStrategy {
@Override
public void action() {
System.out.println("努力写文章");
}
}
。。。。。。
复制代码
策略算法
/**
* @author
* @version Id: ResultHandler.java, v 0.1 2021/5/13 8:21 下午 Exp $$
*/
public class ResultHandler {
private IncreaseFansStrategy increaseFansStrategy;
public ResultHandler(IncreaseFansStrategy increaseFansStrategy){
this.increaseFansStrategy = increaseFansStrategy;
}
// 具体执行的操作方法
public void exec(){
increaseFansStrategy.action();
}
}
复制代码
测试类
/**
* @author
* @version Id: Class.java, v 0.1 2021/5/13 8:24 下午 Exp $$
*/
public class Class {
public static void main(String[] args) {
ResultHandler resultHandler = new ResultHandler(new NavyIHandler());
resultHandler.exec();
System.out.println();
System.out.println();
System.out.println();
System.out.println();
resultHandler = new ResultHandler(new GreatHandler());
resultHandler.exec();
}
}
复制代码
具体执行结果
ps:大家会发现如果策略实现比较多的情况下需要创建很多类,相对繁琐,代价比较大。
总结:
-
优点:
- 算法可以自由切换
- 改一下策略很方便
- 算法可以自由切换
-
扩展性良好
- 增加一个策略,就多增加一个类就好了。(但是同样是缺点)
-
缺点:
- 策略类的数量增多
- 每一个策略都是一个类,复用的可能性很小、类数量增多
- 所有的策略类都需要对外暴露
- 上层模块必须知道有哪些策略,然后才能决定使用哪一个策略
。
。
。
所以杀鸡焉用宰牛刀,在日常开发中很难用到策略模式。但是复杂的if-else又很头痛,因此大部分人选择了妥协。But 新的解决办法来喽
策略模式的变种方法(Map+函数式接口)
样例一 先用string做一个简单的策略校验()
public static void main(String[] args) {
Map<String, Function<String, String>> checkResultDispatcherMuti = new HashMap<>();
checkResultDispatcherMuti.put("校验1", order -> String.format("对%s执行业务逻辑1", order));
checkResultDispatcherMuti.put("校验2", order -> String.format("对%s执行业务逻辑2", order));
checkResultDispatcherMuti.put("校验3", order -> String.format("对%s执行业务逻辑3", order));
checkResultDispatcherMuti.put("校验4", order -> String.format("对%s执行业务逻辑4", order));
checkResultDispatcherMuti.put("校验5", order -> String.format("对%s执行业务逻辑5", order));
checkResultDispatcherMuti.put("校验6", order -> String.format("对%s执行业务逻辑6", order));
checkResultDispatcherMuti.put("校验7", order -> String.format("对%s执行业务逻辑7", order));
checkResultDispatcherMuti.put("校验8", order -> String.format("对%s执行业务逻辑8", order));
checkResultDispatcherMuti.put("校验9", order -> String.format("对%s执行业务逻辑9", order));
//从逻辑分派Dispatcher中获得业务逻辑代码,result变量是一段lambda表达式
Function<String, String> result = checkResultDispatcherMuti.get("校验3");
if (result != null) {
//执行这段表达式获得String类型的结果
System.out.println(result.apply("校验3"));
}else{
System.out.println("错误提示");
}
}
复制代码
样例二:根据不同情况进行参数赋值 ps:(常规策略模式是类基本,会导致类太多,复用性不强,改变之后的为方法级别)
/**
* 策略方法
* @version Id: greatHandler.java, v 0.1 2021/5/13 8:18 下午 wuzhaobo Exp $$
*/
public class TestHandler {
public Function<TestClass, TestClass> strategy1() {
return test -> {
TestClass test1 = new TestClass();
test1.setName("张氏");
test1.setSex(test.getSex());
return test1;
};
}
public Function<TestClass, TestClass> strategy2() {
return test -> {
TestClass test1 = new TestClass();
test1.setName(test.getName());
test1.setSex("10");
return test1;
};
}
}
复制代码
策略实体类
public class TestClass {
private String name;
private String sex;
public String getName() {
return name;
}
public String getSex() {
return sex;
}
public void setName(String name) {
this.name = name;
}
public void setSex(String sex) {
this.sex = sex;
}
}
复制代码
策略封装方法
/**
* 获取策略方法
* @param object
* @return
*/
public static Map<String, Function<Object, Object>> convert(Object object){
Map<String, Function<Object, Object>> implMap = new ConcurrentHashMap<>();
try{
java.lang.Class<?> aClass = object.getClass();
Method[] declaredMethods =aClass.getDeclaredMethods();
for (Method declaredMethod : declaredMethods) {
String name = declaredMethod.getName();
// 去除隐藏的方法
if(name.contains("$")){
System.out.println(1111);
continue;
}
System.out.println(name);
String str = name;
declaredMethod.setAccessible(true);//设置权限
Function<Object, Object> invoke = ( Function<Object, Object>)declaredMethod.invoke(object);
System.out.println("1231313"+invoke);
implMap.put(str,invoke);
}
System.out.println(implMap);
return implMap;
}catch (Exception e){
throw new RuntimeException(e);
}
}
复制代码
测试类
public static void main(String[] args) throws InvocationTargetException, IllegalAccessException {
Map<String, Function<Object, Object>> convert = ParamUtils.convert(new TestHandler());
Function<Object, Object> strategy2 = (Function<Object, Object>) convert.get("strategy2");
System.out.println(strategy2);
TestClass testClass = new TestClass();
testClass.setName("测试");
testClass.setSex("100");
SurrenderOrderDto surrenderOrderDto2 = (SurrenderOrderDto)strategy2.apply(testClass);
System.out.println(surrenderOrderDto2.getOrderNo());
}
复制代码
测试结果
总结
这种方案很好的解决了策略模式带来的弊端。 可以解决常规业务中if-else复杂的情况,不在为if-else和策略模式而纠结。
ps: (还可以用使用Spring提供的扩展点ApplicationContextAware,在系统启动的时候将对应策略方法的存放在Map中,同时对外提供执行入口 execute)