当前位置: 首页 > news >正文

策略模式详情

策略模式:定义一组算法,将每个算法封装起来,使它们可以互相替换,且算法的变换不会影响使用算法的客户。

• 抽象策略(Strategy)类:这是一个抽象角色,通常由一个接口或抽象类实现。此角色给出所有的具体策略类所需的接口。
• 具体策略(Concrete Strategy)类:实现了抽象策略定义的接口,提供具体的算法实现或行为。
• 环境(Context)类:持有一个策略类的引用,最终给客户端调用。


🧩 一、问题背景:传统策略模式的痛点

现在有三种促销策略:

//抽象策略(Strategy)类
public interface Strategy {void show();
}
//具体策略(Concrete Strategy)类
public class StrategyA implements Strategy {public void show() { System.out.println("买一送一"); }
}
public class StrategyB implements Strategy {public void show() { System.out.println("满200减50"); }
}
public class StrategyC implements Strategy {public void show() { System.out.println("加1元换购"); }
}
//定义环境角色(Context):用于连接上下文,即把促销活动推销给客户,这里可以理解为销售员
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) {String festival = "Spring"; // 春节SalesMan man = null;if ("Spring".equals(festival)) {man = new SalesMan(new StrategyA());} else if ("MidAutumn".equals(festival)) {man = new SalesMan(new StrategyB());} else if ("Christmas".equals(festival)) {man = new SalesMan(new StrategyC());}man.salesManShow();}
}

🔴 问题:
上层业务(Client)必须写一堆 if-else 来选择策略。
当节日越来越多,这部分就会变得臃肿、难维护。


🧠 二、引入“简单工厂模式”来“接管 if-else”

定义一个“策略工厂”,专门负责根据条件创建策略对象👇

public class StrategyFactory {public static Strategy getStrategy(String festival) {if ("Spring".equals(festival)) {return new StrategyA();} else if ("MidAutumn".equals(festival)) {return new StrategyB();} else if ("Christmas".equals(festival)) {return new StrategyC();}throw new IllegalArgumentException("未知节日:" + festival);}
}

🏭 三、工厂 + 策略模式结合,解放Client

public class Client {public static void main(String[] args) {String festival = "MidAutumn"; // 中秋节// ✅ 不需要自己写 if-else 了,交给工厂选择策略Strategy strategy = StrategyFactory.getStrategy(festival);// ✅ 策略交给 SalesMan 执行SalesMan man = new SalesMan(strategy);man.salesManShow();}
}

💡 四、理解

「结合 if-else + 工厂模式,把具体的实现类对象交给策略模式,让上游业务调用方解放策略的选择。」

逐字拆解理解:

关键词含义
if-else + 工厂模式工厂内部仍然用 if-else 判断节日类型,但外部业务层不需要管这个判断逻辑了。
把具体的实现类对象交给策略模式工厂负责创建正确的策略(StrategyAStrategyBStrategyC),然后交给 SalesMan 使用。
让上游业务调用方解放策略的选择调用方只需告诉“是什么场景”(如节日名称),不需要自己 new 策略对象,也不需要判断逻辑。

🧱 五、总结一:

把“如何选择策略”的逻辑(if-else)封装进“工厂模式”,
把“如何执行策略”的逻辑封装进“策略模式”,
从而让“上游业务调用方”只需传一个简单参数(如节日名),就能自动使用正确的策略。


🔧 六、去掉 if-else :注册工厂模式+策略模式

为了更优雅,我们甚至可以去掉工厂中的 if-else,用 Map 注册策略。下面举一个生产中二点实例:用注册工厂模式 + 策略模式,解决登录逻辑中 if-else 冗余、扩展性差的问题。


🧩 1. 传统写法

UserService.login()

@Servicepublic class UserService {public LoginResp login(LoginReq loginReq){if ("account".equals(loginReq.getType())) {// 用户名密码登录逻辑} else if ("sms".equals(loginReq.getType())) {// 手机验证码登录逻辑} else if ("we_chat".equals(loginReq.getType())) {// 微信登录逻辑}}}

❌ 主要问题:不符合开闭原则 ,比如:新增 QQ 登录、支付宝登录等要改源码


🧠 2. 改造目标

策略模式 封装各种登录方式的差异,
工厂方法模式 管理和分配不同的策略,
UserService 专注调度,不再关心具体实现。


🧱 3. 模式改造思路

✅ 策略模式负责:

定义一系列「登录方式」算法,让它们可以互相替换,而不影响使用方。

抽象策略类:

public interface UserGranter {LoginResp login(LoginReq loginReq);
}

具体策略实现:

@Component
public class AccountGranter implements UserGranter {@Overridepublic LoginResp login(LoginReq loginReq) {System.out.println("账号密码登录");return new LoginResp();}
}@Component
public class SmsGranter implements UserGranter {@Overridepublic LoginResp login(LoginReq loginReq) {System.out.println("短信验证码登录");return new LoginResp();}
}@Component
public class WeChatGranter implements UserGranter {@Overridepublic LoginResp login(LoginReq loginReq) {System.out.println("微信登录");return new LoginResp();}
}

✅ 注册工厂方法模式负责:

把策略的创建、管理逻辑统一封装起来,解耦外部调用。

@Component
public class UserLoginFactory implements ApplicationContextAware {private static final Map<String, UserGranter> granterPool = new ConcurrentHashMap<>();@Autowiredprivate LoginTypeConfig loginTypeConfig;// 读取配置文件信息,并注入Bean@Overridepublic void setApplicationContext(ApplicationContext applicationContext) throws BeansException {loginTypeConfig.getTypes().forEach((k, v) -> {granterPool.put(k, (UserGranter) applicationContext.getBean(v));});}// 根据登录类型返回对应策略public UserGranter getGranter(String grantType) {return granterPool.get(grantType);}
}

✅ 配置化管理(灵活扩展)

application.yml

login:types:account: accountGrantersms: smsGranterwe_chat: weChatGranter

LoginTypeConfig.java

@Getter
@Setter
@Configuration
@ConfigurationProperties(prefix = "login")
public class LoginTypeConfig {private Map<String, String> types;
}

✅ 改造后的 Service

@Service
public class UserService {@Autowiredprivate UserLoginFactory factory;public LoginResp login(LoginReq loginReq) {UserGranter granter = factory.getGranter(loginReq.getType());if (granter == null) {LoginResp resp = new LoginResp();resp.setSuccess(false);return resp;}return granter.login(loginReq);}
}

🌈 4. 改造效果对比

对比项改造前(if-else)改造后(工厂+策略)
可读性❌ 复杂、混乱✅ 清晰、结构化
扩展性❌ 改源码才能新增登录方式✅ 新增类 + 配置即可
可维护性❌ 所有逻辑堆在一个方法✅ 各登录方式独立实现
复用性❌ 难以单独测试某种登录✅ 可独立注入并复用
设计原则❌ 违反开闭原则✅ 完全符合开闭原则
依赖关系紧耦合松耦合

🚀 6. 扩展新增 QQ 登录

只需要三步 ✅:

① 新增策略类

@Component
public class QQGranter implements UserGranter {@Overridepublic LoginResp login(LoginReq loginReq) {System.out.println("QQ 登录");return new LoginResp();}
}

② 修改配置文件

login:types:account: accountGrantersms: smsGranterwe_chat: weChatGranterqq: qQGranter

③ 不改任何 Java 代码,系统自动支持 QQ 登录 🎉


🌈七、应用

  1. 订单的支付策略
    • 支付宝支付
    • 微信支付
    • 银行卡支付
    • 现金支付
  2. 解析不同类型excel
    • xls格式
    • xlsx格式
  3. 打折促销
    • 满300元9折
    • 满500元8折
    • 满1000元7折
  4. 物流运费阶梯计算
    • 5kg以下
    • 5kg-10kg
    • 10kg-20kg
    • 20kg以上
    一句话总结:只要代码中有冗长的 if-else 或 switch 分支判断都可以采用策略模式优化。

🚀 八、总结二

策略模式封装“行为的变化”,工厂模式封装“对象的创建”,
两者结合可以让Service业务层完全不用管“选哪个策略、怎么 new 策略”,
只需告诉系统“我要处理哪个场景”,系统自动选择并执行正确的行为。

http://www.dtcms.com/a/491197.html

相关文章:

  • 省级网站 开发建设 资质企业员工培训课程
  • VS Code settings.json配置(终端篇)
  • 好看的静态网站网页制作设计教程
  • Spring Boot API文档与自动化测试详解
  • 哈尔滨龙彩做网站多少钱做网站怎么样引流
  • Redis实战篇---添加缓存(店铺类型添加缓存需求)
  • 泸州百拓网站建设英文网站数据库如何建设
  • 河北电子商务网站建设互联网营销策略有哪些
  • 苏州专业网站设计制作公司湛江企业网站怎么建设
  • Linux Docker部署Dify使用shell脚本关闭和开启服务
  • 四川省建设信息网站从点点博客搬家到wordpress
  • 国家变电站声纹监测设备需求总结
  • Python读取Nacos配置时如何设置命名空间
  • 深入网站开发和运维 pdf网站开发文本编辑器
  • ros2—交叉编译ros bridge自定义消息
  • 修复ubuntu22.04检测不到mt7922蓝牙问题
  • C#共用体
  • 正点原子RK3568学习日志7-module_init优先级
  • 脑机接口:BCI2000框架,EEG信号解码?
  • 2025“芯星计划”华中区域启动暨长沙民政加速科技集成电路测试验证实践基地揭牌仪式圆满收官
  • 一般的学校网站怎么做网站主要的设计内容主题
  • docker后端jar包本地构建镜像
  • Spring Boot 3零基础教程,Spring Boot 日志的归档与切割,笔记22
  • 序列化的几种常见方式
  • 蜜罐技术重塑网络安全新格局
  • 做网站需要用到的符号语言基于网站开发的app
  • 企业管理咨询网站gta买办公室网站建设中
  • Python 的 typing 库介绍
  • 33. C++ cout cin 文件IO
  • 腾讯地图时空智能开放平台MCP接入说明