Java 设计模式--策略模式,枚举+工厂方法实现

如果项目中的一个页面跳转功能存在10个以上的if else判断,想要做一下整改

1|0一、什么是策略模式

策略模式是对算法的包装,是把使用算法的责任和算法本身分割开来,委派给不同的对象管理,最终可以实现解决多重If判断问题。

1.环境(Context)角色:持有一个Strategy的引用。

2.抽象策略(Strategy)角色:这是一个抽象角色,通常由一个接口或抽象类实现。此角色给出所有的具体策略类所需的接口。

3.具体策略(ConcreteStrategy)角色:包装了相关的算法或行为。

(定义策略接口→实现不同的策略类→利用多态或其他方式调用策略。)

2|0二、策略模式优缺点

2|1优点:

算法可以自由切换(高层屏蔽算法,角色自由切换)

避免使用多重条件判断(如果算法过多就会出现很多相同的判断,很难维护)

扩展性好(可自由添加取消算法,而不影响整个功能)

2|2缺点:

策略数量增多(每一个策略类复用性小,如果需要增加算法,就只能新增类)

所有的策略类都需要对外暴露(使用的人必须了解使用策略,这个就需要其他模式来补充,比如工厂模式、代理模式)

3|0三、代码示例

img

3|11.定义共同的方法和行为

package com.ultiwill.strategy; public interface PayStrategy { /** * 共同的行为方法 * @return */ String toPayHtml(); }

3|22. 三种具体策略的实现 (阿里支付, 微信支付, 小米支付)

package com.ultiwill.strategy.impl; import com.ultiwill.strategy.PayStrategy; /** * @author chong.zuo * @date 2020/9/24 15:21 */ public class AliPayStrategy implements PayStrategy { @Override public String toPayHtml() { return "调用阿里支付...AliPayStrategy"; } }
package com.ultiwill.strategy.impl; import com.ultiwill.strategy.PayStrategy; /** * @author chong.zuo * @date 2020/9/24 15:29 */ public class WeChatPayStrategy implements PayStrategy { @Override public String toPayHtml() { return "调用微信支付...WeChatPayStrategy"; } }
package com.ultiwill.strategy.impl; import com.ultiwill.strategy.PayStrategy; /** * @author chong.zuo * @date 2020/9/24 15:34 */ public class XiaomiPayStrategy implements PayStrategy { @Override public String toPayHtml() { return "调用小米支付...XiaomiPayStrategy"; } }

3|33. 枚举类定义映射地址

package com.ultiwill.strategy.enums; import org.apache.commons.lang.StringUtils; /** * 枚举 * @author chong.zuo * @date 2020/9/24 15:45 */ public enum PayEnumStrategy { /** * 阿里支付 */ ALI_PAY("1","com.ultiwill.strategy.impl.AliPayStrategy"), /** * 微信支付 */ WECHAT_PAY("2","com.ultiwill.strategy.impl.WeChatPayStrategy"), /** * 小米支付 */ XIAOMI_PAY("3","com.ultiwill.strategy.impl.XiaomiPayStrategy"); private String code; private String className; PayEnumStrategy() { } PayEnumStrategy(String code, String className) { this.code = code; this.className = className; } public static String getClassNameByCode(String code) { String className = ""; if (StringUtils.isEmpty(code)) { return className; } for (PayEnumStrategy e : PayEnumStrategy.values()) { if (e.code.equalsIgnoreCase(code)) { className = e.className; break; } } return className; } public String getCode() { return code; } public String getClassName() { return className; } }

3|44.工厂类反射执行

package com.ultiwill.strategy.factory; import com.ultiwill.strategy.PayStrategy; import com.ultiwill.strategy.enums.PayEnumStrategy; /** * @author chong.zuo * @date 2020/9/24 16:10 */ public class StrategyFactory { /** * 使用策略工厂获取具体策略实现 * @param code * @return */ public static PayStrategy getPayStrategy(String code) { try { return (PayStrategy) Class.forName(PayEnumStrategy.getClassNameByCode(code)).newInstance(); } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } return null; } }

3|55.上下文获取具体策略

package com.ultiwill.strategy.context; import com.ultiwill.strategy.PayStrategy; import com.ultiwill.strategy.enums.PayEnumStrategy; import com.ultiwill.strategy.factory.StrategyFactory; import org.apache.commons.lang.StringUtils; /** * 上下文 * * @author chong.zuo * @date 2020/9/24 15:41 */ public class PayContextStrategy { /** * 获取具体的策略实现 * * @param code * @return */ public static String toPayHtml(String code) { if (StringUtils.isBlank(code)) { return "code不能为空..."; } PayStrategy payStrategy = StrategyFactory.getPayStrategy(code); if (payStrategy == null) { return "没有找到具体的策略..."; } return payStrategy.toPayHtml(); } }

4|0四、测试

controller:

package com.ultiwill.controller; import com.ultiwill.strategy.context.PayContextStrategy; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; /** * @author c * @date 2020/5/14 9:59 */ @RestController public class TestController { @RequestMapping("/helloworld") public String hello(String code) { return PayContextStrategy.toPayHtml(code); /*if ("0".equals(code)) { return "调用阿里支付...AliPayStrategy"; } else if ("1".equals(code)) { return "调用微信支付...AliPayStrategy"; } else if ("2".equals(code)) { return "调用小米支付...AliPayStrategy"; } return "调用接口不存在"; */ } }

pom:

<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.ultiwill</groupId> <artifactId>springboot-jar</artifactId> <version>1.0-SNAPSHOT</version> <packaging>jar</packaging> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-tomcat</artifactId> <version>2.1.8.RELEASE</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> <version>2.1.8.RELEASE</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <version>2.1.8.RELEASE</version> </dependency> <dependency> <groupId>commons-lang</groupId> <artifactId>commons-lang</artifactId> <version>2.6</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <configuration> <source>1.8</source> <target>1.8</target> <encoding>UTF-8</encoding> </configuration> </plugin> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <version>2.1.8.RELEASE</version> <configuration> <includeSystemScope>true</includeSystemScope> <mainClass>com.ultiwill.Application</mainClass> </configuration> <executions> <execution> <goals> <goal>repackage</goal> </goals> </execution> </executions> </plugin> </plugins> </build> </project>

img

5|0五、结果

img

img

6|0六、在spring中通过Autowired注解实现策略模式

6|1使用AutowireCapableBeanFactory手动注入

使用.newInstance();创建对象的话,如果其他对象都使用Spring Autowired,还需要手动创建所有依赖的Bean:

private @Autowired AutowireCapableBeanFactory beanFactory; public void process() { MyBean obj = new MyBean(); beanFactory.autowireBean(obj); // obj will now have its dependencies autowired. }

本例中可以使用

private @Autowired AutowireCapableBeanFactory beanFactory; /** * 使用策略工厂获取具体策略实现 * @param code * @return */ public PayStrategy getPayStrategy(String code) { String className = PayEnumStrategy.getClassNameByCode(code); try { PayStrategy str = (PayStrategy) Class.forName(className).getDeclaredConstructor().newInstance(); beanFactory.autowireBean(str); return str; } catch (InstantiationException | NoSuchMethodException | ClassNotFoundException | IllegalAccessException | InvocationTargetException e) { e.printStackTrace(); } return null; }

6|2使用Map<String,?> 自动注入

先附上如下的代码:

public interface TalkService { void talk(String content); }
@Service(value = "withSisterTalkService") public class WithSisterTalkService implements TalkService { @Override public void talk(String content) { System.out.println(this.getClass().getName() + ":" + content); } }
@Service(value = "withGirlFriendTalkService") public class WithGirlFriendTalkService implements TalkService { @Override public void talk(String content) { System.out.println(this.getClass().getName() + ":" + content); } }
@Service public class TalkServiceStrategyContext implements TalkService { private Map<String, TalkService> strategyMap = new ConcurrentHashMap<>(); @Autowired public TalkServiceStrategyContext(Map<String, TalkService> strategyMap) { this.strategyMap.clear(); this.strategyMap.putAll(strategyMap); } @Override public void talk(String content) { } }

注意,这里必须是Map<String, TalkService>类型!

@Autowired private Map<String, TalkService> talkServiceMap; @GetMapping(value = "doTest") public String doTest() { Set<String> strings = talkServiceMap.keySet(); for (String string : strings) { System.out.println(string + ":" + talkServiceMap.get(string).toString()); } return this.getClass().getName(); }

其访问测试controller后,打印的信息如下:

talkServiceStrategyContext:com.haiyang.onlinejava.complier.service.impl.TalkServiceStrategyContext@2f0b1419 withGirlFriendTalkService:com.haiyang.onlinejava.complier.service.impl.WithGirlFriendTalkService@1cf19a02 withSisterTalkService:com.haiyang.onlinejava.complier.service.impl.WithSisterTalkService@1ef3c76d

看了后感觉很奇怪,在上方只定义了一个map<String,TalkService>的map,居然它就能自动找到实现了TalkService的所有bean,并将service的beanName作为了key,感觉还是牛逼啊,spring的注解居然还能这样用。

然后简单看了下Autowired的源码,其javaDoc文档里也有说明:

package org.springframework.beans.factory.annotation; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * Marks a constructor, field, setter method or config method as to be * autowired by Spring's dependency injection facilities. * * <p>Only one constructor (at max) of any given bean class may carry this * annotation, indicating the constructor to autowire when used as a Spring * bean. Such a constructor does not have to be public. * * <p>Fields are injected right after construction of a bean, before any * config methods are invoked. Such a config field does not have to be public. * * <p>Config methods may have an arbitrary name and any number of arguments; * each of those arguments will be autowired with a matching bean in the * Spring container. Bean property setter methods are effectively just * a special case of such a general config method. Such config methods * do not have to be public. * * <p>In the case of multiple argument methods, the 'required' parameter is * applicable for all arguments. * * <p>In case of a {@link java.util.Collection} or {@link java.util.Map} * dependency type, the container will autowire all beans matching the * declared value type. In case of a Map, the keys must be declared as * type String and will be resolved to the corresponding bean names. * * <p>Note that actual injection is performed through a * {@link org.springframework.beans.factory.config.BeanPostProcessor * BeanPostProcessor} which in turn means that you <em>cannot</em> * use {@code @Autowired} to inject references into * {@link org.springframework.beans.factory.config.BeanPostProcessor * BeanPostProcessor} or * {@link org.springframework.beans.factory.config.BeanFactoryPostProcessor BeanFactoryPostProcessor} * types. Please consult the javadoc for the {@link AutowiredAnnotationBeanPostProcessor} * class (which, by default, checks for the presence of this annotation). * * @author Juergen Hoeller * @author Mark Fisher * @since 2.5 * @see AutowiredAnnotationBeanPostProcessor * @see Qualifier * @see Value */ @Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Autowired { /** * Declares whether the annotated dependency is required. * <p>Defaults to {@code true}. */ boolean required() default true; }

关注这句:

In case of a java.util.Collection or java.util.Map dependency type, the container will autowire all beans matching the declared value type. In case of a Map, the keys must be declared as type String and will be resolved to the corresponding bean names.

它大致是说Autowired当使用在Collection里时,会将所申明类的所有实现类都放在那个指定的Collection里;

如果Autowired和map使用的话呢,它将它bean的名称作为key,所有的bean作为value.

6|3使用Set<?>自动注入

如果不想使用bean的名字作为map的Key的话,我们可以自定义寻址方式,自动注入时候使用Set<?>:

public interface Strategy { void doStuff(); StrategyName getStrategyName(); } public enum StrategyName { StrategyA, StrategyB, StrategyC }
@Component public class StrategyA implements Strategy{ @Override public void doStuff() { //implement algorithm A here } @Override public StrategyName getStrategyName() { return StrategyName.StrategyA; } } @Component public class StrategyB implements Strategy{ @Override public void doStuff() { //implement algorithm B here } @Override public StrategyName getStrategyName() { return StrategyName.StrategyB; } } @Component public class StrategyC implements Strategy{ @Override public void doStuff() { } @Override public StrategyName getStrategyName() { return StrategyName.StrategyC; } }
import java.util.HashMap; import java.util.Map; import java.util.Set; import org.springframework.stereotype.Component; @Component public class StrategyFactory { private Map<StrategyName, Strategy> strategies; @Autowired public StrategyFactory(Set<Strategy> strategySet) { createStrategy(strategySet); } public Strategy findStrategy(StrategyName strategyName) { return strategies.get(strategyName); } private void createStrategy(Set<Strategy> strategySet) { strategies = new HashMap<StrategyName, Strategy>(); strategySet.forEach( strategy ->strategies.put(strategy.getStrategyName(), strategy)); } }

Now we can inject StrategyFactory using @Autowired annotation. Here is the sample code using our StrategyFactory.

import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @Service public class SomeService { @Autowired private StrategyFactory strategyFactory; public void findSome(){ // Now get the strategy by passing the name Strategy strategy = strategyFactory.findStrategy(StrategyName.StrategyA); // you can now call the methods defined in strategy. strategy.doStuff(); } }

__EOF__

本文作者咫尺是梦
本文链接https://www.cnblogs.com/satire/p/14620980.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   satire  阅读(3907)  评论(0编辑  收藏  举报
编辑推荐:
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· AI 智能体引爆开源社区「GitHub 热点速览」
· 写一个简单的SQL生成工具
点击右上角即可分享
微信分享提示