[设计模式] ------ 策略模式实战:java中替代if-else的大段逻辑

java中用策略模式替代if-else的大段逻辑

问题:

java本来是一门以面向对象为主的语言,但很多人嘴上说着java面向对象,然后除了表映射实体之外,其他的还是面向过程的思路。
就比如今天要说的,代码中大段大段的if-else判断,每个if-else代码块中又有很多复杂的逻辑,导致整个代码混乱不堪,让别人看到就感觉看到屎一样的代码一样。

那么,如何优雅的替代这些代码呢,其实有个设计模式(策略模式)就很好的解决了这个问题。

情景举例:

比如说订单处理,订单会有多个type状态,比如说
type=1的时候,就执行普通订单的逻辑;
type=2的时候,就执行满减促销订单的逻辑;
type=3的时候,就执行满返促销订单的逻辑;
等等,可能type会有数十种甚至更多种情况。

然后有些人就会开始if-else了,比如有如下的伪代码:

if(type=1){普通订单...(此处省略100多行处理逻辑)
}else if(type=2){满减促销订单...(此处省略100多行处理逻辑)
}else if(type=3){满返促销订单...(此处省略100多行处理逻辑)
}else if(type=n){...(此处省略几百上千行的逻辑)
}

做的好点的,会把if-else代码块中的逻辑,抽成一个个的方法,会稍微显的代码清晰许多,但这些都是面向过程的思想。
我认为,这种情况就应该用以下这种方式,即,用策略模式代替if-else,真正做到面向对象。
把每种不同类型的订单抽成一个个对象,然后通过不同的注解标识来区分调用。

用策略模式代替if-else:

首先,本次例子用的是Spring-Boot框架,亲测没问题。SpringMVC框架应该也是没问题的。
定义一个订单类,里面有type属性,type可以是"1"、“2”、“3”…
定义一个抽象类AbstractHandler,里面有个抽象方法handle,入参是订单类
定义一个注解HandlerType,有个value属性,value是几就代表这个注解注的这个类是什么类型的订单
定义普通类HandlerImpl01,实现AbstractHandler,代表普通订单,即@HandlerType(“1”);
定义普通类HandlerImpl02,实现AbstractHandler,代表满减促销订单,即@HandlerType(“2”);
定义普通类HandlerImpl03,实现AbstractHandler,代表满返促销订单,即@HandlerType(“3”);
定义一个初始化类HandlerProcessor,实现BeanFactoryPostProcessor,过程如下:
1、找到带有注解@HandlerType的类,
2、以注解的值为key,对应的类为value,存在一个map中
3、将这个map作为构造函数的参数,初始化HandlerContext,将HandlerContext注册到spring中成为一个单例bean。
很明显,目的就是为了保存不同type对应的不同类。
定义类HandlerContext,有个map类型的属性叫handlerMap,有个getInstance的方法,入参是type,返回AbstractHandler。
最后使用的时候,是先调用handlerContext.getInstance方法,根据type获取对应的AbstractHandler。
然后再调用他的handle方法,执行对应订单类型的处理逻辑。

具体代码如下:

//@Data是lombok的注解,为了省略不写get/set方法
@Data
public class OrderDTO {private String code;private BigDecimal price;/*** 订单类型* 1:普通订单* 2:满减订单* 3:满返订单*/private String type;
}
public abstract class AbstractHandler {abstract public String handle(OrderDTO orderDTO);
}
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface HandlerType {String value();
}
@Component
@HandlerType("1")
public class HandlerImpl01 extends AbstractHandler {@Overridepublic String handle(OrderDTO orderDTO) {System.out.println("处理type为1的订单,orderDTO.type="+orderDTO.getType());return "success";}
}
@Component
@HandlerType("2")
public class HandlerImpl02 extends AbstractHandler {@Overridepublic String handle(OrderDTO orderDTO) {System.out.println("处理type为2的订单,orderDTO.type="+orderDTO.getType());return "success";}
}
@Component
@HandlerType("3")
public class HandlerImpl03 extends AbstractHandler {@Overridepublic String handle(OrderDTO orderDTO) {System.out.println("处理type为3的订单,orderDTO.type="+orderDTO.getType());return "success";}
}
@Component
@SuppressWarnings("unchecked")
public class HandlerProcessor implements BeanFactoryPostProcessor {//这里是具体的handler策略类的包的位置,为了后面的包扫描private static final String HANDLER_PACKAGE = "com.zs.handler";@Overridepublic void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {Map<String, Class> handlerMap = new HashMap<>();//包扫描ClassScaner.scan(HANDLER_PACKAGE,HandlerType.class).forEach(clazz ->{Annotation annotation = clazz.getAnnotation(HandlerType.class);HandlerType handlerType = (HandlerType) annotation;String type = handlerType.value();System.out.println(type);handlerMap.put(type,clazz);});HandlerContext handlerContext = new HandlerContext(handlerMap);//注册单例beanFactory.registerSingleton(HandlerContext.class.getName(),handlerContext);}
}
public class HandlerContext {private Map<String,Class> handlerMap;public HandlerContext(Map<String, Class> handlerMap) {this.handlerMap = handlerMap;}public AbstractHandler getInstance(String type){Class clazz = handlerMap.get(type);if(clazz == null){throw new IllegalArgumentException("没有type对应的处理器,type:"+type);}return (AbstractHandler)SpringContextUtils.getBean(clazz);}
}

定义接口:

public interface OrderService {/*** 根据订单类型处理订单* @param orderDTO* @return*/String handle(OrderDTO orderDTO);
}

实现接口:

@Service
public class OrderServiceImpl implements OrderService {@Resourceprivate HandlerContext handlerContext;@Overridepublic String handle(OrderDTO orderDTO) {System.out.println("OrderServiceImpl handle 方法开始执行===");AbstractHandler handler = handlerContext.getInstance(orderDTO.getType());return handler.handle(orderDTO);}
}

包扫描的工具类:

public class ClassScaner implements ResourceLoaderAware {private ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();private final List<TypeFilter> includeFilters = new LinkedList<TypeFilter>();private final List<TypeFilter> excludeFilters = new LinkedList<TypeFilter>();private MetadataReaderFactory metadataReaderFactory = new CachingMetadataReaderFactory(this.resourcePatternResolver);public ClassScaner() {}public void setResourceLoader(ResourceLoader resourceLoader) {this.resourcePatternResolver = ResourcePatternUtils.getResourcePatternResolver(resourceLoader);this.metadataReaderFactory = new CachingMetadataReaderFactory( resourceLoader);}//    public final ResourceLoader getResourceLoader() {
//        return this.resourcePatternResolver;
//    }public void addIncludeFilter(TypeFilter includeFilter) {this.includeFilters.add(includeFilter);}//    public void addExcludeFilter(TypeFilter excludeFilter) {
//        this.excludeFilters.add(0, excludeFilter);
//    }//    public void resetFilters(boolean useDefaultFilters) {
//        this.includeFilters.clear();
//        this.excludeFilters.clear();
//    }public static Set<Class> scan(String basePackage, Class<? extends Annotation>... annotations) {ClassScaner cs = new ClassScaner();for (Class anno : annotations)cs.addIncludeFilter(new AnnotationTypeFilter(anno));return cs.doScan(basePackage);}//    public static Set<Class> scan(String[] basePackages, Class<? extends Annotation>... annotations) {
//        ClassScaner cs = new ClassScaner();
//        for (Class anno : annotations)
//            cs.addIncludeFilter(new AnnotationTypeFilter(anno));
//        Set<Class> classes = new HashSet<Class>();
//        for (String s : basePackages)
//            classes.addAll(cs.doScan(s));
//        return classes;
//    }public Set<Class> doScan(String basePackage) {Set<Class> classes = new HashSet<Class>();try {String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX+ org.springframework.util.ClassUtils.convertClassNameToResourcePath(SystemPropertyUtils.resolvePlaceholders(basePackage))+ "/**/*.class";Resource[] resources = this.resourcePatternResolver.getResources(packageSearchPath);for (int i = 0; i < resources.length; i++) {Resource resource = resources[i];if (resource.isReadable()) {MetadataReader metadataReader = this.metadataReaderFactory.getMetadataReader(resource);if ((includeFilters.size() == 0 && excludeFilters.size() == 0)|| matches(metadataReader)) {try {classes.add(Class.forName(metadataReader.getClassMetadata().getClassName()));} catch (ClassNotFoundException e) {e.printStackTrace();}}}}} catch (IOException ex) {throw new BeanDefinitionStoreException("I/O failure during classpath scanning", ex);}return classes;}protected boolean matches(MetadataReader metadataReader) throws IOException {for (TypeFilter tf : this.excludeFilters) {if (tf.match(metadataReader, this.metadataReaderFactory)) {return false;}}for (TypeFilter tf : this.includeFilters) {if (tf.match(metadataReader, this.metadataReaderFactory)) {return true;}}return false;}
}

spring工具类,为了获取bean

@Component
public class SpringContextUtils implements ApplicationContextAware {private static ApplicationContext applicationContext = null;public static ApplicationContext getApplicationContext(){return applicationContext;}@SuppressWarnings("unchecked")public static <T> T getBean(String beanId) {return (T) applicationContext.getBean(beanId);}public static <T> T getBean(Class<T> requiredType) {return (T) applicationContext.getBean(requiredType);}@Overridepublic void setApplicationContext(ApplicationContext applicationContext) throws BeansException {SpringContextUtils.applicationContext = applicationContext;}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/499805.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

mongodb 索引详解

使用springboot连接mongodb的时候&#xff0c;涉及到索引的使用 举例&#xff1a; Document(collection"book") //注释的是复合索引 //CompoundIndexes( // { // CompoundIndex(name "复合索引名字",def "{字段01:1,字段02:…

[转载] --- 让线程按顺序执行8种方法

看到一篇比较用心的总结&#xff0c;涉及到很多知识点&#xff0c;转来保存&#xff0c;而且我把里面的每个方法都试了一遍&#xff0c;亲测没问题 此次转载&#xff0c;还新增了一些说明和结构 我的总结&#xff1a; 其实&#xff0c;让线程按顺序执行&#xff0c;其实就是…

mongodb数据库,批量插入性能测试记录

spring boot 框架下&#xff0c;操作mongodb数据库 maven&#xff1a;spring-data-mongodb:2.1.3.RELEASE mongo数据库用的是本地的mongo&#xff0c;所以环境不一样&#xff0c;可能结果不一样。但趋势应该是一样的。 测试保证每次批量插入时&#xff0c;库里的数据量都是一…

[转载] --- 数据库基本知识

里面的很多点&#xff0c;我之前都总结过&#xff0c;但是感觉这篇把这些都连起来了&#xff0c;总结的挺好&#xff0c;转载保存一下 【从入门到入土】令人脱发的数据库底层设计前言 说到数据库这个词&#xff0c;我只能用爱恨交加这个词来形容它。两年前在自己还单纯懵懂的时…

java中使用lua脚本

第一步&#xff1a; windows下&#xff0c;先下载安装lua&#xff08;其他操作系统自行百度&#xff0c;我只说主要基本的流程&#xff09; 下载地址 我选了lua-5.3.4_Win64_bin.zip为例 第二步&#xff1a; 解压到D盘根路径的lua文件夹中 配置环境变量&#xff0c;增加D:\l…

java中使用lua操作redis

java中使用lua脚本参见我的上一篇文章 lua基础 本篇简单说下java中使用lua操作redis的示例&#xff0c;如下&#xff1a; 先引入jedis <dependency><groupId>redis.clients</groupId><artifactId>jedis</artifactId><version>2.9.0</…

spring-boot发送邮件失败 AuthenticationFailedException: 535 Authentication Failed

发送邮件失败&#xff0c;平时一直是好的&#xff0c;突然有天开始失败了&#xff0c;最后是发现邮箱密码失效了。。。 有的邮箱&#xff0c;需要定期更改密码。

windows本地项目开机自启动设置

记录下&#xff0c;本地项目开机自启动 vue项目 新建vue.bat文件 echo off :: nodejs安装目录下的nodevars.bat set nodevars "D:\Program Files\nodejs\nodevars.bat" :: 切换到D盘 d: :: 移动到需要启动的目录 cd D:\Users\curry.zhang\IdeaProjects\data-chec…

互联网广告行业(01)------ 初识了解DSP、SSP、ADX

最近有幸接触到公司的一个实时竞价系统&#xff0c;也算是公司的核心系统之一了&#xff0c;增加了很多新的知识&#xff0c;可能有点乱&#xff0c;先总结一波&#xff1a; 广告行业&#xff0c;先介绍概念 广告主&#xff1a;需要打广告的站点&#xff0c;一般就是卖东西的…

互联网广告行业(02)------OpenRTB(实时竞价)规范解读

RTB&#xff1a;(Real Time Bidding实时竞价)&#xff0c;RTB是一种广告交易的方式 OpenRTB&#xff1a;简单理解就是一个行业规范&#xff0c;是一个为了促进RTB方式广告的标准&#xff0c;有对应的api文档&#xff0c;大家都按照这个规范去传参数&#xff0c;那么发送方和接收…

[go]---从java到go(01)---基础与入门上手

为什么用go&#xff0c;就是为了快速响应并且高并发。 一样的逻辑&#xff0c;用java也能实现&#xff0c;但用go可能就比java快点。 如果你很熟练java了&#xff0c;那么学习go就会很快。 go的社区环境相比java没那么大&#xff0c;但一般问题都足够了。 go是谷歌出品&#xf…

[go]---从java到go(02)---一个简单的handler模式的实现

类似于责任链模式吧&#xff0c;不同类实现相同的入参&#xff0c;执行不同的操作&#xff0c;一个执行完再确定要不要执行下一个。 用go实现&#xff1a; 1.定义一个接口 后面所有的handler都要实现这个接口的handler方法 type IHandler interface {/**true 表示通过 false…

[数据库] --- clickhouse

clickhouse是一个列式数据库&#xff08;系统&#xff09;。 官方文档 官网比较全&#xff0c;但也可以说比较杂&#xff0c;下面就是我个人的一些总结&#xff0c;以及在实际工作中的应用场景。 1.clickhouse适用场景 clickhouse主要适合那种大量数据做分析的场景。 一般数据…

错误记录:expected single matching bean but found 2

springboot项目&#xff0c;之前有mysql数据源&#xff0c;现在又新增了clickhouse数据源&#xff0c;于是 新增了一个clickhouseDatasource的配置bean&#xff0c;如下&#xff1a; Beanpublic DataSource dataSource() throws PropertyVetoException {HikariConfig config …

消息队列(5):RocketMQ

介绍 RocketMQ是一款成熟的分布式消息中间件。 由阿里2012年开源&#xff0c;2017年成为Apache顶级项目。 源码是java写的。 高性能&#xff0c;低延迟&#xff0c;高可靠。历经多次双十一大促&#xff0c;整体很稳定。 RocketMQ对比其他mq的优势 对比kafka和Rabbitmq&#…

[错误记录] --- clickhouse报错Decimal value is too small

java操作clickhouse数据库&#xff0c;执行insert的时候&#xff0c;报错&#xff1a; Exception in thread "main" ru.yandex.clickhouse.except.ClickHouseException: ClickHouse exception, code: 69, host: xx.xx.xx.xxx, port: xxxx; Code: 69, e.displayText(…

[错误记录] --- rocketmq批量消费设置参数的问题

rocketmq想支持批量消费&#xff0c;于是便设置以下参数&#xff1a; consumer.setConsumeMessageBatchMaxSize(1000);这样是正确的&#xff0c;但由于业务要求&#xff0c;还想再设置大点&#xff0c;于是设置成这样&#xff1a; consumer.setConsumeMessageBatchMaxSize(10…

rocketmq批量消费

rocketmq默认就是可以批量消费的&#xff0c;但需要设置多个参数一起配合。 我们只需要知道他是怎么消费的&#xff0c;就可以很精准的设置他的批量消费参数。 我们看看DefaultMQPushConsumer源码中的这几个参数&#xff1a; /*** 消费消息线程&#xff0c;最小数目*/private …

阿波罗配置中心(apollo)的个人看法

阿波罗应该是近几年比较火的一个分布式配置中心了&#xff0c;说说我个人的理解&#xff0c;希望对一些人有用吧。 首先从使用者的角度想 我们怎么用配置中心的&#xff1f; 1.得有个页面&#xff0c;能有权限管理&#xff0c;能有创建配置key-value。 在阿波罗中&#xff…

架构师成长之路

一个架构师要掌握的最基本的数学知识 知识点备注log三角函数二项式定理多项式合并极限幂函数泰勒级数傅里叶级数概率