Java设计模式-策略模式-基于Spring实现
1、策略模式
1.1、概述
策略模式是一种行为设计模式,它允许在运行时选择算法的行为。它将算法封装在独立的策略类中,使得它们可以相互替换,而不影响客户端代码。这种模式通过将算法的选择从客户端代码中分离出来,提供了更大的灵活性和可维护性。
在Java中,策略模式的设计理念可以通过以下步骤实现:
- 定义一个策略接口(或抽象类),该接口定义了所有具体策略类都必须实现的方法。
- 创建具体的策略类,实现策略接口,并提供具体的算法实现。
- 在客户端代码中,创建一个策略对象,并将其传递给需要使用算法的对象。
- 客户端对象使用策略对象来执行特定的算法。
在你提供的代码片段中,我无法确定与策略模式相关的代码。如果你有更多的上下文或示例代码,我可以更好地帮助你理解和应用策略模式。
1.2、优缺点
策略模式具有以下优点:
- 可以代替if-else
-
算法的独立性:策略模式将算法封装在独立的策略类中,使得算法可以独立于客户端代码进行修改和扩展。这样可以提高代码的灵活性和可维护性。
-
可替换性:由于策略模式将算法封装在不同的策略类中,因此可以在运行时动态地切换和替换算法。这样可以根据不同的需求选择最合适的算法,而无需修改客户端代码。
-
单一职责原则:策略模式将不同的算法封装在不同的策略类中,使得每个策略类只负责一个具体的算法。这样符合单一职责原则,提高了代码的可读性和可维护性。
-
扩展性:由于策略模式将算法封装在独立的策略类中,因此可以很容易地添加新的策略类来扩展系统的功能。
策略模式也有一些缺点:
-
增加了类的数量:使用策略模式会增加系统中的类的数量,因为每个具体的算法都需要一个对应的策略类。这可能会增加代码的复杂性和理解难度。
-
客户端必须了解所有的策略类:客户端必须了解所有可用的策略类,并在运行时选择合适的策略。这可能会增加客户端代码的复杂性。
-
策略切换的开销:在运行时切换策略可能会带来一定的开销,特别是在需要频繁切换策略的情况下。这可能会影响系统的性能。
综上所述,策略模式在提供灵活性、可维护性和可扩展性方面具有很多优点,但也需要权衡其增加的类数量和策略切换的开销。在设计和使用策略模式时,需要根据具体的需求和情况进行权衡和选择。
2、SpringBean方式实现
- bean的名字(默认):实现策略类的名字首字母小写
2.1、实现步奏
- 可以看到,去获取bean是需要用户自己去做的。
2.2、实现
①定义策略接口
package com.cc.eed.strategy; /** * <p>基于SpringBean的策略模式</p> * * @author CC * @since 2023/10/13 */ public interface ISpringBeanStrategy { /** * 吃饭 */ String eating(); /** * 玩 */ String play(); }
②定义实现类1
package com.cc.eed.strategy.impl; import com.cc.eed.strategy.ISpringBeanStrategy; import org.springframework.stereotype.Component; /** * <p>小美</p> * * @author CC * @since 2023/10/13 */ @Component public class MeiSpringBeanImpl implements ISpringBeanStrategy { /** * 吃饭 */ @Override public String eating() { return "小美,吃饭!"; } /** * 玩 */ @Override public String play() { return "小美,玩!"; } }
定义实现类2
package com.cc.eed.strategy.impl; import com.cc.eed.strategy.ISpringBeanStrategy; import org.springframework.stereotype.Component; /** * <p>小明</p> * * @author CC * @since 2023/10/13 */ @Component public class MingSpringBeanImpl implements ISpringBeanStrategy { /** * 吃饭 */ @Override public String eating() { return "小明,吃饭!"; } /** * 玩 */ @Override public String play() { return "小明,玩!"; } }
③定义beanName的枚举
- 用于使用类型int获取对应的beanName
package com.cc.eed.enums; import lombok.Getter; import org.springframework.util.Assert; import java.util.Arrays; /** * <p></p> * * @author CC * @since 2023/10/13 */ @Getter public enum PeopleEnum { MING(1, "小明", "mingSpringBeanImpl"), MEI(2, "小美", "meiSpringBeanImpl") ; public Integer type; public String name; public String beanName; /** <p>根据类型获取beanName<p> * @param type type * @return {@link String} * @since 2023/10/13 * @author CC **/ public static String getBeanName(Integer type) { PeopleEnum peopleEnum = Arrays.stream(values()) .filter(p -> p.getType().equals(type)) .findAny().orElse(null); Assert.notNull(peopleEnum, "暂不支持的策略模式!"); return peopleEnum.getBeanName(); } PeopleEnum(Integer type, String name, String beanName) { this.type = type; this.name = name; this.beanName = beanName; } public void setType(Integer type) { this.type = type; } public void setName(String name) { this.name = name; } public void setBeanName(String beanName) { this.beanName = beanName; } }
④使用springBean工具类获取beanName
⑤使用
- 传入不同的类型,获取不同的策略
@Test public void test02()throws Exception{ //根据BeanName获取具体的bean,实现策略模式 //根据人员ID(或者类型)获取不同的bean String beanName = PeopleEnum.getBeanName(1); ISpringBeanStrategy bean = (ISpringBeanStrategy) SpringBeanUtil.getBean(beanName); String eating = bean.eating(); System.out.println(eating); String play = bean.play(); System.out.println(play); }
- 结果:
传入1
传入2
传入除了1/2的
3、简单工厂模式实现(推荐)
- 加自定义Bean的名字
3.1、实现步奏
- 可以看到,去获取bean是交给工厂去做的,用户只需要传入类型即可。
3.2、实现
①定义策略接口
package com.cc.eed.strategy; /** * <p>简单工厂模式 - 实现的策略模式</p> * * @author CC * @since 2023/10/13 */ public interface IFactoryStrategy { /** * 吃饭 */ String eating(); /** * 玩 */ String play(); }
②生产策略bean的工厂
- 由于使用的@Resource注解,BUSINESS_FACTORY会自动注入所有实现了IFactoryStrategy接口的Bean。@Resource注解是Spring提供的一种依赖注入的方式,它会根据类型进行自动装配。在这个例子中,BUSINESS_FACTORY是一个Map类型的成员变量,它的键是字符串类型,值是IFactoryStrategy类型。当Spring容器启动时,会扫描并找到所有实现了IFactoryStrategy接口的Bean,并将它们自动注入到BUSINESS_FACTORY中。
- 由于BUSINESS_FACTORY使用了ConcurrentHashMap作为实现,它会根据PirateEnum.values().length的大小来初始化容量。这样可以确保BUSINESS_FACTORY的大小与实际注入的Bean数量一致,提高性能和效率。
package com.cc.eed.strategy; import com.cc.eed.enums.PirateEnum; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Component; import org.springframework.util.Assert; import javax.annotation.Resource; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; /** * <p>简单工厂</p> * <li>可以生产多个策略</li> * * @author CC * @since 2023/10/13 */ @Component public class StrategyByFactory { /** * 1、批量注入实现了 IFactoryStrategy 的Bean。 * 2、用bean的数量当做map的大小 */ @Resource private final Map<String, IFactoryStrategy> BUSINESS_FACTORY = new ConcurrentHashMap<>(PirateEnum.values().length); //生成的策略... /** <p>根据类获取不同的Bean<p> * @param type type * @return {@link IFactoryStrategy} * @since 2023/10/13 * @author CC **/ public IFactoryStrategy getBusinessMap(Integer type){ Assert.notNull(type, "类型不能为空!"); String beanName = PirateEnum.getBeanName(type); return BUSINESS_FACTORY.get(beanName); } //生成的其他策略... }
③策略实现类1
package com.cc.eed.strategy.impl; import com.cc.eed.enums.PirateEnum; import com.cc.eed.strategy.IFactoryStrategy; import org.springframework.stereotype.Component; /** * <p>路飞</p> * * @author CC * @since 2023/10/13 */ @Component(PirateEnum.LF_BEAN_NAME) public class LuFeiFactoryStrategy implements IFactoryStrategy { /** * 吃饭 */ @Override public String eating() { return "路飞,吃饭!"; } /** * 玩 */ @Override public String play() { return "路飞,玩!"; } }
③策略实现类2
package com.cc.eed.strategy.impl; import com.cc.eed.enums.PirateEnum; import com.cc.eed.strategy.IFactoryStrategy; import org.springframework.stereotype.Component; /** * <p>明哥</p> * * @author CC * @since 2023/10/13 */ @Component(PirateEnum.MG_BEAN_NAME) public class MingGgFactoryStrategy implements IFactoryStrategy { /** * 吃饭 */ @Override public String eating() { return "明哥,吃饭!"; } /** * 玩 */ @Override public String play() { return "明哥,玩!"; } }
④定义beanName的枚举
package com.cc.eed.enums; import org.springframework.util.Assert; import java.util.Arrays; /** * <p></p> * * @author CC * @since 2023/10/13 */ public enum PirateEnum { MG(11, "明哥", PirateEnum.MG_BEAN_NAME), LF(22, "路飞", PirateEnum.LF_BEAN_NAME) ; public Integer type; public String name; public String beanName; /** * 自定义的beanName */ public static final String MG_BEAN_NAME = "mingGg_11"; public static final String LF_BEAN_NAME = "luFei_22"; /** <p>根据类型获取beanName<p> * @param type type * @return {@link String} * @since 2023/10/13 * @author CC **/ public static String getBeanName(Integer type) { PirateEnum pirateEnum = Arrays.stream(values()) .filter(p -> p.getType().equals(type)) .findAny().orElse(null); Assert.notNull(pirateEnum, "暂不支持的策略模式!"); return pirateEnum.getBeanName(); } PirateEnum(Integer type, String name, String beanName) { this.type = type; this.name = name; this.beanName = beanName; } public Integer getType() { return type; } public void setType(Integer type) { this.type = type; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getBeanName() { return beanName; } public void setBeanName(String beanName) { this.beanName = beanName; } }
⑤使用springBean工具类获取beanName
⑥使用
- 需要注入工厂来调用方法即可
@Resource private StrategyByFactory strategyByFactory; @Test public void test03()throws Exception{ //使用简单工厂生产的策略模式 —— 明显发现使用起来更简单,把创建bean的权利交给了简单工厂 IFactoryStrategy businessMap = strategyByFactory.getBusinessMap(33); System.out.println(businessMap.eating()); System.out.println(businessMap.play()); }
结果:
-
传入:11
-
传入:22
-
传入:33
4、总结-工具类
- 实现的重点在于获取Spring的bean
- 工具类:使用springBean工具类获取beanName:
见:https://www.cnblogs.com/kakarotto-chen/p/17760069.html - 项目:https://gitee.com/KakarottoChen/blog-code.git
的:DesignDemo
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!