策略模式真的像你想的那么简单么?

简介: 策略模式的使用与进阶

介绍

在策略模式中,我们创建表示各种策略的对象和一个行为随着策略对象改变而改变的 context 对象。策略对象改变 context 对象的执行算法。

优点: 1、算法可以自由切换。 2、避免使用多重条件判断。 3、扩展性良好。

缺点: 1、策略类会增多。 2、所有策略类都需要对外暴露。

举例:诸葛亮给了赵云三个锦囊,每个锦囊都有一个策略。选择交通工具汽车、火车、飞机都是一种策略。

注意事项:

当我们的if else中需要处理很多业务,就可以考虑使用策略模式;

如果一个系统的策略过多,就需要考虑使用混合模式,解决策略类膨胀的问题。

实现

这里以不同等级的客户购买物品时对应不同的折扣为例,先创建一个接口

publicinterfaceStrategy {
DoublecalculatePrice(Doubleprice,Integeramount);
}

具体的策略类

publicclassGeneralCustomerimplementsStrategy {
@OverridepublicDoublecalculatePrice(Doubleprice, Integeramount) {
returnprice*amount;
    }
}
publicclassSilverCustomerimplementsStrategy {
@OverridepublicDoublecalculatePrice(Doubleprice, Integeramount) {
returnprice*amount*0.99;
    }
}
publicclassGoldCustomerimplementsStrategy {
@OverridepublicDoublecalculatePrice(Doubleprice, Integeramount) {
returnprice*amount*0.97;
    }
}

创建 Context 类。上下文并不执行任务, 而是负责与具体的策略类交互。这就好比出行工具有汽车、火车、飞机,但是谁去开火车?就是这个context

publicclassContext {
privateStrategystrategy;
publicContext(Strategystrategy){
this.strategy=strategy;
 }
publicDoubleexecuteStrategy(Doubleprice,Integeramount){
returnstrategy.calculatePrice(price, amount);
 }
}

使用

publicclassStrategyDemo {
publicstaticvoidmain(String[] args) {
doubletotalPrice=0;
Contextcontext=newContext(newGeneralCustomer());
totalPrice=context.executeStrategy(15.0, 2);
System.out.println(totalPrice);
Contextcontext1=newContext(newSilverCustomer());
totalPrice=context1.executeStrategy(15.0,2);
System.out.println(totalPrice);
Contextcontext2=newContext(newGoldCustomer());
totalPrice=context2.executeStrategy(15.0, 2);
System.out.println(totalPrice);
    }
}

问题

1、还是要去if else 判断客户的等级然后决定使用哪种策略,如何解决这个问题?

2、策略类会增多,业务逻辑分散到各个实现类中,而且没有一个地方可以俯视整个业务逻辑

扩展

策略工厂模式

上一篇我们说到的工厂模式,我给一个标识,由工厂帮我创建对应的产品,那么结合到这里是不是就可以解决上述问题? 我给工厂一个客户等级标识,它会给你对应的策略。

创建策略工厂,Context类就用不上了:

publicclassStrategyFactory {
publicstaticStrategyexecute(IntegerlevelCode){
Strategystrategy=null;
switch (levelCode){
case1:
strategy=newGeneralCustomer();
break;
case2:
strategy=newSilverCustomer();
break;
case3:
strategy=newGoldCustomer();
break;
default:
thrownewIllegalArgumentException("客户等级错误");
        }
returnstrategy;
    }
}

使用

publicclassStrategyDemo {
publicstaticvoidmain(String[] args) {
//接口参数获取用户等级 传给策略工厂即可Strategygeneral=StrategyFactory.execute(1);
general.calculatePrice(15.0,2);
    }
}

如上我们可以解决第一个问题,那么第二个问题又该如何解决,策略类膨胀会导致项目非常臃肿。

Map+函数式接口

此时我们把上诉的策略类全部作为方法

@ServicepublicclassCustomerStrategyService {
publicDoublegeneralCustomer(Doubleprice, Integeramount) {
returnprice*amount;
        }
publicDoublesilverCustomer(Doubleprice, Integeramount) {
returnprice*amount*0.99;
        }
publicDoublegoldCustomer(Doubleprice, Integeramount) {
returnprice*amount*0.97;
        }
    }
Map+Function优化@ServicepublicclassPriceService {
@AutowiredprivateCustomerStrategyServicecustomerStrategyService;
@FunctionalInterfaceinterfaceCalculateFunction<A,B>{
publicDoublecalculate(Aa,Bb);
    }
privatefinalMap<Integer, CalculateFunction<Double,Integer>>strategyMap=newHashMap<>();
/*** 初始化策略*/@PostConstructpublicvoidinitDispatcher(){
strategyMap.put(1,(price,amount) ->customerStrategyService.generalCustomer(price,amount));
strategyMap.put(2,(price,amount) ->customerStrategyService.silverCustomer(price,amount));
strategyMap.put(3,(price,amount) ->customerStrategyService.goldCustomer(price,amount));
    }
publicDoublecalculatePrice(IntegercustomerLevel,Doubleprice,Integeramount){
CalculateFunction<Double, Integer>function=strategyMap.get(customerLevel);
//这里防止客户等级没找到匹配的,可以使用断言来判断从而抛出统一异常returnfunction.calculate(price,amount);
    }
}

搞个接口试试

@RestControllerpublicclassTestController {
@AutowiredprivatePriceServicepriceService;
@RequestMapping("/calculatePrice")
publicDoublecalculatePrice(IntegercustomerLevel,Doubleprice,Integeramount){
returnpriceService.calculatePrice(customerLevel, price, amount);
    }
}

总结

少于4种情况且每种情况对应的业务逻辑不复杂那肯定首选if else,

有4-6种情况且业务逻辑不复杂,建议使用switch case

业务逻辑复杂的情况下,可使用策略工厂模式或者Map+Function,自行选择。

相关文章
链接远程服务器出现 Connection closed by foreign host
链接远程服务器出现 Connection closed by foreign host
|
5月前
|
缓存 JSON 安全
如何修复 Postman 中的 403 Forbidden 错误
当执行请求时,出现 403 Forbidden 错误可能有几个原因,在本文中,我们将学习如何修复来自 Postman 的 403 错误。
|
11月前
|
Rust 网络协议 应用服务中间件
granian:让你的 Web 应用程序快如闪电
granian:让你的 Web 应用程序快如闪电
742 2
|
移动开发 资源调度 JavaScript
Vue移动端网页(H5)预览pdf文件(pdfh5和vue-pdf)
这篇文章介绍了在Vue移动端网页中使用`pdfh5`和`vue-pdf`两个插件来实现PDF文件的预览,包括滚动查看、缩放、添加水印、分页加载、跳转指定页数等功能。
8044 1
Vue移动端网页(H5)预览pdf文件(pdfh5和vue-pdf)
|
存储 Java Spring
SpringBoot异步任务获取HttpServletRequest
这样的操作对于保持异步操作中的请求上下文十分有用,特别是当你需要在日志记录、权限检查或者其他需要请求信息的场景中。确保上下文的正确传递和管理对于构建可靠的,异步处理能力很强的Spring Boot应用至关重要。
781 3
|
监控 云计算 开发者
探索云计算中的无服务器架构:从概念到实践
无服务器架构作为云计算领域的新兴技术,正在以其高效、灵活的特性吸引着越来越多的开发者和企业。本文将深入探讨无服务器架构的概念及其在云计算中的应用,通过实际案例展示如何利用无服务器架构构建可靠、可扩展的应用系统。
5款最受欢迎的邮件营销系统有什么?
以下是5款热门邮件营销系统:Mailchimp以其易用性和多功能性领先;蜂邮EDM提供专业级邮件创建体验;Sendinblue结合了邮件与短信营销,适合中小企业;GetResponse是全面的营销平台,包含自动化功能;AokSend是老牌系统,提供丰富模板和分析工具,支持API接口。选择合适系统能提升营销效果。
|
数据采集 Java 数据安全/隐私保护
使用Java进行网络采集:代理IP与参数传递详解
Java参数传递是按值传递,包括对象引用的值。当传递对象时,方法内部修改对象内容会影响原始对象,但不能改变原始引用。示例展示了如何在爬虫代理中使用此机制,通过`ProxySettings`类传递代理信息,方法内可访问但不能更改原始对象。理解这一机制对编写高效无错的Java代码至关重要。
106 0
使用Java进行网络采集:代理IP与参数传递详解
|
前端开发 流计算
css:text-decoration给文字增加上划线、删除线、下划线
css:text-decoration给文字增加上划线、删除线、下划线
2932 0
css:text-decoration给文字增加上划线、删除线、下划线
|
C语言 程序员
【C语言基础教程】内存的申请和释放(malloc、free、realloc、calloc)
【C语言基础教程】内存的申请和释放(malloc、free、realloc、calloc)
2523 0
【C语言基础教程】内存的申请和释放(malloc、free、realloc、calloc)