Spring中如何优雅的使用策略模式
创作时间:
作者:
@小白创作中心
Spring中如何优雅的使用策略模式
引用
CSDN
1.
https://blog.csdn.net/weixin_46425661/article/details/140872043
策略模式是一种行为设计模式,它允许一个对象在运行时选择多种行为之一。在Spring框架中,策略模式可以通过定义接口和多个实现类来实现,同时使用Spring的依赖注入功能来管理这些实现类。本文将详细介绍策略模式的概念、结构、优缺点和使用场景,并通过一个具体的折扣计算业务场景,展示如何在Spring框架中实现策略模式。
一、策略模式是什么?
定义
策略模式定义了一系列算法,并将每个算法封装起来,使它们可以相互替换,且算法的变化不会影响使用算法的客户(满足开闭原则)。策略模式属于对象行为模式,它通过对算法进行封装,把使用算法的责任和算法的实现分割开来,并委派给不同的对象对这些算法进行管理。
结构
策略模式的主要角色如下:
- 抽象策略(Strategy)类:这是一个抽象角色,通常由一个接口或抽象类实现。此角色给出所有的具体策略类所需的接口。
- 具体策略(Concrete Strategy)类:实现了抽象策略定义的接口,提供具体的算法实现或行为。
- 环境(Context)类:持有一个策略类的引用,最终给客户端调用。
简单类图如下:
定义一个抽象策略(Strategy)类接口Strategy:
public interface Strategy {
void show();
}
定义具体策略角色(Concrete Strategy):
public class StrategyA implements Strategy {
public void show() {
System.out.println("StrategyA.show()");
}
}
//为中秋准备的促销活动B
public class StrategyB implements Strategy {
public void show() {
System.out.println("StrategyB.show()");
}
}
//为圣诞准备的促销活动C
public class StrategyC implements Strategy {
public void show() {
System.out.println("StrategyC.show()");
}
}
定义环境角色(Context)类SalesMan:
public class SalesMan {
//持有抽象策略角色的引用
private Strategy strategy;
public SalesMan(Strategy strategy) {
this.strategy = strategy;
}
//调用具体实现类
public void salesManShow(){
strategy.show();
}
}
客户端代码:
public class Client {
public static void main(String[] args) {
SalesMan salesMan = new SalesMan(new StrategyA());
salesMan.salesManShow();
System.out.println("==============");
salesMan.setStrategy(new StrategyB());
salesMan.salesManShow();
System.out.println("==============");
salesMan.setStrategy(new StrategyC());
salesMan.salesManShow();
}
}
二、策略模式的优缺点与使用场景
优缺点
优点:
- 策略类之间可以自由切换
由于策略类都实现同一个接口,所以使它们之间可以自由切换。 - 易于扩展
增加一个新的策略只需要添加一个具体的策略类即可,基本不需要改变原有的代码,符合“开闭原则“ - 避免使用多重条件选择语句(if else),充分体现面向对象设计思想。
缺点:
- 客户端必须知道所有的策略类,并自行决定使用哪一个策略类。
- 策略模式将造成产生很多策略类(可以通过使用享元模式在一定程度上减少对象的数量)
使用场景
- 一个系统需要动态地在几种算法中选择一种时,可将每个算法封装到策略类中。
- 一个类定义了多种行为,并且这些行为在这个类的操作中以多个条件语句的形式出现,可将每个条件分支移入它们各自的策略类中以代替这些条件语句。
- 系统中各算法彼此完全独立,且要求对客户隐藏具体算法的实现细节时。
- 系统要求使用算法的客户不应该知道其操作的数据时,可使用策略模式来隐藏与算法相关的数据结构。
- 多个类只区别在表现行为不同,可以使用策略模式,在运行时动态选择具体要执行的行为。
- 总之当出现如 if…else 语句、switch…case 语句时,就可以使用策略模式消除多重条件语句。
三、在Spring中使用策略模式
如现在有一个计算折扣的业务场景,并且需要动态的使用多种折扣策略计算。
1.抽象策略类
代码如下(示例):
import java.util.List;
import java.util.Map;
public interface DiscountService {
List getDiscountResult(Map<String, Object> params);
}
2.具体策略类
代码如下(示例):
import com.qbh.design.strategy.DiscountService;
import org.springframework.stereotype.Component;
import java.util.Collections;
import java.util.List;
import java.util.Map;
@Component
public class DiscountAServiceImpl implements DiscountService {
@Override
public List getDiscountResult(Map<String, Object> params) {
//进行折扣相关业务处理
System.out.println("执行折扣A");
return Collections.emptyList();
}
}
@Component
public class DiscountBServiceImpl implements DiscountService {
@Override
public List getDiscountResult(Map<String, Object> params) {
//进行折扣相关业务处理
System.out.println("执行折扣B");
return Collections.emptyList();
}
}
@Component
public class DiscountCServiceImpl implements DiscountService {
@Override
public List getDiscountResult(Map<String, Object> params) {
//进行折扣相关业务处理
System.out.println("执行折扣C");
return Collections.emptyList();
}
}
3.环境类
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.Map;
@Component
public class DiscountContext {
//当一个接口有多个实现类时,Spring会自动将Strategy接口的实现类注入到这个Map中,key为bean id,value值则为对应的策略实现类
@Autowired
private Map<String, DiscountService> discountServiceMap;
/**
*
* @param key 实现类名
* @param params 参数
* @return
*/
public List execute(String key, Map<String, Object> params) {
DiscountService service = discountServiceMap.get(key);
return service.getDiscountResult(params);
}
}
在以上 DiscountContext 类中,Spring 会自动将 DiscountService 接口的实现类注入到discountServiceMap这个Map中。
前提是实现 DiscountService 接口的实现类得是交给Spring 容器管理的。
discountServiceMap的key值就是实现类的 bean id,也可以用@Component(“value”)的方式设置,像我上面直接用默认的方式的话,就是首字母小写。value值则为对应的策略实现类。
4.枚举
import cn.hutool.core.util.ObjectUtil;
public enum DiscountEnum {
strategyA("discountA", "discountAServiceImpl"),
strategyB("discountB", "discountBServiceImpl"),
strategyC("discountC", "discountCServiceImpl"),;
private String name;
private String discountImplName;
public static String getDiscountImplName(String name) {
if (ObjectUtil.isEmpty(name)) {
return null;
}
for (DiscountEnum aEnum : DiscountEnum.values()) {
if (aEnum.getName().equals(name)) {

return aEnum.getDiscountImplName();
}
}
return null;
}
DiscountEnum(String name, String discountImplName) {
this.name = name;
this.discountImplName = discountImplName;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDiscountImplName() {
return discountImplName;
}
public void setDiscountImplName(String discountImplName) {
this.discountImplName = discountImplName;
}
}
5.服务类
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class TestServiceImpl extends ServiceImpl<TestMapper, Test> implements ITestService {
@Autowired
private DiscountContext discountContext;
@Override
public void test(String name,Map<String, Object> params) {
discountContext.execute(DiscountEnum.getDiscountImplName(name),params);
}
}
热门推荐
咖啡渣的"逆袭":从废弃物到电池材料
这个叫“马齿苋”
剑桥雅思7听力如何精准捕捉答案?
【释法时刻】非机动车之间发生交通事故咋赔?
姓张的女生名字诗经
你踩的越用力,地面压力越大?关于压强的那些事儿
营养食谱科学搭配
人身损害赔偿的法律依据有哪些
羽毛球比赛最稳定的打法,羽毛球单打战术必胜绝招都有哪些?
打工人实用指南!你的外卖点对了吗?
宝宝发热时应吃什么
儿童艾灸的好处和坏处
广东被重罚,朱芳雨深夜发声!哈雷尔自宣加盟新疆,王少杰最新伤情
【澳洲留学费用】留学澳洲需要多少费用?学费/住宿/生活费等费用估算
自行车变速器调节全攻略:从基础到进阶的完整指南
首购指南:10万/15万/20万预算最佳车型推荐
说话的最高境界:《非暴力沟通》
如何通过PSN DNS设置访问港服?
俄语副动词的核心语法知识
驾照到期了怎样进行续期操作?续期时需要准备哪些材料?
如何正确地在网上举报违法犯罪行为的人
古玩辨伪之铜香炉6
中医流派及其代表人物,一文说尽
UC走过二十周年,雷军晒出当年团队合影
Cell:刘陈立/肖意传团队成功合成高效抗肿瘤合成细菌,并揭示背后的关键原理
上海住房公积金提取条件及步骤是什么
员工工时统计表如何与工资计算关联?
金城武个人资料:多才多艺的天秤座男神
黄精的功效与作用适合哪些人(黄精的功效)
美国垃圾分类完全指南:初到美国必看,轻松掌握分类规则!