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);
}
}
热门推荐
委托他人上牌的流程
宋亚轩首演历史人物,能否超越苏有朋?
爱因斯坦发明了什么?爱因斯坦的故事
深层解读相对论,爱因斯坦到底是如何推导出相对论的?
阿尔伯特·爱因斯坦:照亮20世纪物理学天空的巨星
爱因斯坦晚年之谜:说“一切都是安排好的”,他究竟意指何物?
磁铁的“手”和“性格”:一个有趣的磁铁极性科普实验
溥仪:三次登基与退位的传奇人生
【先驱科研人】通过“有机宇宙化学”探索生命起源,从极微量的火星粒子中找出微生物
NASA重大发现:好奇号在火星探测到30亿年前沉积岩中的有机分子
孔子点赞《关雎》,《诗经》首篇的秘密
赵季平版《关雎》:传统与现代的完美融合
《关雎》:君子淑女的爱情故事
《关雎》语言艺术:重章叠句、双声叠韵与比兴意境之美
肠胃炎的原因是啥症状
紫金山冬日打卡指南:中山陵、明孝陵、美龄宫一站搞定
2025香港春节烟花汇演:时间、地点、看点全攻略
2025香港春节维多利亚港烟花秀:时间、地点、交通全攻略
曲阜三孔:团建活动的理想选择
曲阜团建的好地方|曲阜适合团建的场地推荐
张杰《蝴蝶知道她爱谁》:一只蝴蝶的承诺
张杰OST霸屏,《择天记》和《三生三世》谁更燃?
除夕到了!这份上海年夜饭美食菜单请收好
过年回家带啥上海特产?当地人推荐10种,好吃不贵,多数人没吃过
《甄嬛传》:雍正真的最爱的是纯元皇后吗?
安庆小年:传统与现代交织的年味
安徽小年:二十三还是二十四?南北习俗大不同
安徽小年习俗大盘点:你家啥时候过?
安徽小年习俗揭秘:灶王爷的秘密任务
温彬:央行设立买断式逆回购操作工具的内涵与影响