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);
}
}
热门推荐
理性分析双色球历史数据分析随机性背后的模式
红烧肉的做法
屈原的端午节:古诗词里的爱国情怀
一次性戒烟危险吗?这两种戒烟方式不可取,反而有害健康
秋天拍婚纱照去哪里拍好看(推荐几个秋季婚纱摄影胜地)
立秋季节的奇妙变化
屈原:端午节最忙的男人?
从屈原到赛龙舟:端午节传统民俗的新时代传承
儿童被锁车内悲剧:谁之过?
梨炖陈皮的全方位功效:养生、食疗与作用解析
梨炖陈皮怎么做?功效、做法全解析:梨子炖陈皮、梨煮陈皮教程
一种基于X射线图像的带壳核桃内部干瘪程度检测方法
张家朗:胜利不仅在于金牌,更在于不断超越自我
如何培养谦虚的品质:从《道德经》到现代实践
中型SUV内饰养护全攻略:皮革保护篇
春游太湖畔:十里明珠堤与蠡堤的春日绝美打卡攻略
三月苏州园林打卡指南:拙政园&留园
春天来苏州,这些地方你不能错过!
朱棣叛父辱兄,骗弟欺侄,他篡了22年帝位,还明朝一个超级盛世
喝酒喝多了喝什么饮料解酒
汽车保养必读!火花塞更换周期与维护要点
在法国独自旅行–必备指南
三月赏花背后,《诗经》里的浪漫传说
三月赏梅拍樱攻略:捕捉最美瞬间
三月三赏樱正当时,别错过最美花期!
鞠婧祎科学减重法走红,粉丝打卡效果惊人!
朱元璋为何不抹掉乞丐出身?从历史视角解读这位开国皇帝的独特之处
从孤儿到开国皇帝:朱元璋的早年经历及其影响
金融从业者必修课:跌停盘操作策略与风险管理
量化监管下,A股跌停潮原因揭秘