【设计模式】策略模式+工厂模式动态绑定类名的几种方式
策略模式说明
在策略模式(Strategy Pattern)中,一个类的行为或其算法可以在运行时更改。这种类型的设计模式属于行为型模式。
在策略模式中,我们创建表示各种策略的对象和一个行为随着策略对象改变而改变的 context 对象。策略对象改变 context 对象的执行算法。
这样,在后续调整渠道时只需要新增/删除一个类,而不会影响整体的逻辑。
改造优化
在类中购物渠道分为拼多多、淘系、京东和当当这4个渠道。以前的做法是根据不同的渠道,if/else判断取调用不同的代码。
还有种情况是这几个渠道消费相同的mq,原本的写法是建立4个消费者对应4个类来消费数据,然后根据其中的某个渠道来过滤和调用对应的实现类。
这种方法不仅类很多,而且会占用mq的信道,在后续维护也会很麻烦。
在这种业务情景下,可以由一个入口,然后根据不同的名称路由到不同的类,那么这就很适合用策略模式了。
策略中new出来的类的问题
在很多文章中Context类中,在具体绑定关系时用不同的名称new出不同的方法。举例如下:
这样做的问题在于,当使用例如下面的CacheClusterService交给Spring管理的类时,new出来的就无法正常使用,会抛空指针的错误。
switch (strategyType) {case "add":strategy = new ConcreteStrategyAdd();break;case "sub":strategy = new ConcreteStrategySub();break;case "mul":strategy = new ConcreteStrategyMul();break;
}
中间件相关的类
那么我们就模拟一个交由Spring管理的类来获取值,看是否能正常获取。
缓存
模拟使用缓存获取值。
@Component
@Slf4j
public class CacheClusterService {public String getValue(String key){//模拟获取redis的值return key;}
}
策略模式
策略模式仍然是一个接口,然后实现接口,从而达到抽象的目的。
public interface IStrategyHard1 {enum StrategyType{PDD,TX,JD}String buy();String getType();
}
以下方法实现这个接口:
京东渠道
@Service
public class JdBuyChannel implements IStrategyHard1{@Resourceprivate CacheClusterService cacheClusterService;@Overridepublic String buy() {return cacheClusterService.getValue("jd");}@Overridepublic String getType() {return StrategyType.JD.name();}
}
拼多多渠道
@Service
public class PddBuyChannel implements IStrategyHard1{@Resourceprivate CacheClusterService cacheClusterService;@Overridepublic String buy() {return cacheClusterService.getValue("pdd");}@Overridepublic String getType() {return StrategyType.PDD.name();}
}
淘系渠道
@Service
public class TxBuyChannel implements IStrategyHard1{@Resourceprivate CacheClusterService cacheClusterService;@Overridepublic String buy() {return cacheClusterService.getValue("tx");}@Overridepublic String getType() {return IStrategyHard1.StrategyType.TX.name();}
}
创建Context类
目前实现的方法有好几种方式,主要在于和工厂模式的结合。
实现方式1:实现ApplicationContextAware接口
用ApplicationContextAware,当一个类实现了这个接口(ApplicationContextAware)之后,这个类就可以方便获得ApplicationContext中的所有bean。换句话说,就是这个类可以直接获取spring配置文件中,所有有引用到的bean对象。
在方法中重写setApplicationContext()方法,用applicationContext获取接口的各个实现类,然后绑定类名别名与类。
@Slf4j
@Component
public class OnlineBuyChannelContext implements ApplicationContextAware {private Map<String,IStrategyHard1> BUY_CHANNEL_MAP = new ConcurrentHashMap<>();private ApplicationContext applicationContext;@Overridepublic void setApplicationContext(ApplicationContext applicationContext) throws BeansException {this.applicationContext = applicationContext;Map<String,IStrategyHard1> beanTypes = applicationContext.getBeansOfType(IStrategyHard1.class);log.info("beanTypes:{}",beanTypes);beanTypes.entrySet().forEach(entry->{BUY_CHANNEL_MAP.put(entry.getValue().getType(),entry.getValue());});}public IStrategyHard1 getBuyChannel(String type){if (StringUtils.isBlank(type)){throw new RuntimeException("type is null");}return BUY_CHANNEL_MAP.get(type);}
}
实现方式2:各实现类使用@PostConstruct初始化
用@PostConstruct,实现Spring初始化时将类名别名与类绑定。
@Slf4j
@Component
public class OnlineBuyChannelContext2 {private Map<String, IStrategyHard2> BUY_CHANNEL_MAP2 = new ConcurrentHashMap<>();public void register(String type,IStrategyHard2 iStrategyHard2Impl){if (StringUtils.isBlank(type)){throw new RuntimeException("type is null");}BUY_CHANNEL_MAP2.put(type,iStrategyHard2Impl);}public IStrategyHard2 getBuyChannel(String type){if (StringUtils.isBlank(type)){throw new RuntimeException("type is null");}return BUY_CHANNEL_MAP2.get(type);}
}
在每个方法中使用@PostConstruct,不再使用IStrategyHard1接口的String getType()方法。
举例:
@Service
public class JdBuyChannel2 implements IStrategyHard2 {@Resourceprivate CacheClusterService2 cacheClusterService2;@Resourceprivate OnlineBuyChannelContext2 onlineBuyChannelContext2;@Overridepublic String buy() {return cacheClusterService2.getValue("jd");}@PostConstructpublic void init(){onlineBuyChannelContext2.register(IStrategyHard2.StrategyType.JD.name(),this);}
}
实现方式3:实现InitializingBean接口
此处使用的是实现InitializingBean接口,然后重写afterPropertiesSet()方法。
注意该方法会在spring容器启动初始化bean,即各个策略类完成后调用afterPropertiesSet
方法里调用register
方法。
@Service
public class JdBuyChannel3 implements IStrategyHard3, InitializingBean {@Resourceprivate CacheClusterService3 cacheClusterService3;@Resourceprivate OnlineBuyChannelContext3 onlineBuyChannelContext3;@Overridepublic String buy() {return cacheClusterService3.getValue("jd");}@Overridepublic void afterPropertiesSet() throws Exception {onlineBuyChannelContext3.register(StrategyType.JD.name(),this);}
}
测试类
经过测试,这几种方式都可以实现方法。
@RestController
@Slf4j
public class TestController {@Autowiredprivate OnlineBuyChannelContext onlineBuyChannelContext;@Autowiredprivate OnlineBuyChannelContext2 onlineBuyChannelContext2;/*** 策略模式1:直接用applicationContext注册* @return String*/@GetMapping("/test/getBuy")public String getBuy(){IStrategyHard1 iStrategyHard1 = onlineBuyChannelContext.getBuyChannel(IStrategyHard1.StrategyType.DD.name());String buyChannel = iStrategyHard1.buy();log.info("buyChannel:{}",buyChannel);return buyChannel;}/*** 策略模式2:用@PostConstruct注册* @return String*/@GetMapping("/test/getBuy2")public String getBuy2(){IStrategyHard2 iStrategyHard2 = onlineBuyChannelContext2.getBuyChannel(IStrategyHard2.StrategyType.DD.name());String buyChannel = iStrategyHard2.buy();log.info("buyChannel:{}",buyChannel);return buyChannel;}
}
复盘
- 通过以上方法会发现,实现工厂模式都是与Spring结合的,即在初始化时实现绑定关系;
- 实现策略模式,可实现不同的路由,对后续调整业务的影响会很小;
【设计模式】策略模式+工厂模式动态绑定类名的几种方式相关推荐
- 常用设计模式-策略模式+工厂模式+模板模式(使用场景、解决方案)
在策略模式+工厂模式中,没有使用到模板模式,因为张三和李四的业务逻辑都是调用AAA方法,如果现在在增加一个方法,次方法只需要李四一人去实现BBB方法,此时张三的handel中就会报错,需要张三也去实现 ...
- Java设计模式之策略模式+工厂模式+模板模式
Java设计模式之策略模式+工厂模式+模板模式 1.策略模式+工厂模式+模板模式 个人的理解:实际开发工程中,一些业务很复杂的逻辑使用很多的 if 或者 if···else 语句,不利于维护和扩展,为 ...
- 设计模式——单例模式、工厂模式
设计模式--单例模式.工厂模式 一.六大设计原则 开闭原则(Open Close Principle) 开闭原则就是说对扩展开放,对修改关闭.在程序需要进行拓展的时候,不能去修改原有的代码,实现一个热 ...
- getinstance方法详解_二、设计模式总览及工厂模式详解
二.架构师内功心法之设计模式 2.架构师内功心法之设计模式 2.1.课程目标 1.通过对本章内容的学习,了解设计模式的由来. 2.介绍设计模式能帮我们解决哪些问题. 3.剖析工厂模式的历史由来及应用场 ...
- 实践:使用Spring 原生注解来快速实现 策略模式 + 工厂模式
作者:Richard_Yi juejin.im/post/5db0e910518825648f2ef355 前言 这阵子在做项目组重构的工作,工作中的一部分就是就目前代码库中与企业交互的逻辑抽离出来, ...
- 设计模式之四(抽象工厂模式第三回合)
原文:设计模式之四(抽象工厂模式第三回合) 前言 抽象工厂模式:提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类. 抽象工厂模式最大的好处便是易于交换产品系列,由于具体工厂类,例如I ...
- 软件设计模式“单例模式”和“工厂模式”
软件设计模式"单例模式"和"工厂模式" 单例模式 什么是单例模式 单例模式的实现方式有哪些 单例模式的优缺点 单例模式的应用场景 总结 工厂模式 什么是工厂模式 ...
- 设计模式 - 学习笔记 - 工厂模式Factory Pattern
设计模式 - 学习笔记 - 工厂模式Factory Pattern 1. 简单工厂 1.1 应用场景 1.2 UML 1.3 优劣分析 好处 缺点 1.4 代码示例 抽象产品 AbstractProd ...
- 设计模式-04抽象工厂模式
设计模式-04抽象工厂模式 文章中涉及到的代码,请自行下载 https://gitee.com/pan_xiao_lei123/designmode.git 前面介绍的工厂方法模式中考虑的是一类产品的 ...
最新文章
- AI又进阶!除了鉴别PS图片,还能一键卸妆
- 本地运行hadoop
- Elastic Search学习笔记5——基本操作
- swagger-bootstrap-ui 1.9.3 发布,i18n及自定义文档支持
- 垃圾收集器–串行,并行,CMS,G1(以及Java 8中的新增功能)
- cocos2d-x3.0 相对布局(一)
- 【算法设计与分析】06 几类重要的函数
- lamp ci框架 php配置文件,LAMP环境搭建
- Android 基本测试工具的使用
- 计算机与农学论文,农学毕业论文范文
- Naive-UI,尤大推荐的Vue组件库
- 2022新版iApp工具箱源码+有聊天系统等等
- Matlab saveas 函数批量保存彩色eps图片到指定路径
- 相似度算法--莱文斯坦距离加入同义词逻辑
- 春季儿童吃什么有助于长高,3款适合孩子长高的食谱做法,学起来
- 2.1立即数的判断方法一
- 强大的文本编辑器EmEditor最新版分享
- 指南解读:急性心力衰竭中国急诊管理指南(2022)
- Elasticsearch: analyzer
- Benewake(北醒) 短距 TF-Luna 8m介绍以及资料整理
热门文章
- java swing 皮肤_Java swing皮肤(look and feel)大全
- 笔记本触摸键盘驱动自动禁用_如何为iPad的蓝牙键盘禁用自动更正
- 配置springcloud中eureka服务访问时需要用户名密码
- cvCopy和 cvCloneImage
- 小红书koc/kol种草指标,实操经验
- 德语翻译器在线翻译中文
- 财富赢家模拟炒股最新版分享,股市小散的利器
- 高德地图导航功能与讯飞语音开发遇到的坑
- JavaScript => 解决 Unexpected lexical declaration in case block 的问题
- crmeb pro v1.2 人人分销模式下,分销员管理列表显示问题