避免过多if - else的新姿势:策略模式、工厂 + 策略
目录
前言
需求
编码实现
思考
策略模式
什么是策略模式?
编码
深思
工厂 + 策略
toMap
效果
后续
前言
避免过多if - else的新姿势:卫语句、小函数、多态、反射
在之前文章说到,简单 if-else
,可以使用 卫语句
进行优化。但是在实际开发中,往往不是简单 if-else
结构,我们通常会不经意间
写下如下代码:
-------------------- 理想中的 if-else --------------------
public void today() {if (isWeekend()) {System.out.println("玩游戏");} else {System.out.println("上班!");}
}-------------------- 现实中的 if-else --------------------if (money >= 1000) {if (type == UserType.SILVER_VIP.getCode()) {System.out.println("白银会员 优惠50元");result = money - 50;} else if (type == UserType.GOLD_VIP.getCode()) {System.out.println("黄金会员 8折");result = money * 0.8;} else if (type == UserType.PLATINUM_VIP.getCode()) {System.out.println("白金会员 优惠50元,再打7折");result = (money - 50) * 0.7;} else {System.out.println("普通会员 不打折");result = money;}
}//省略 n 个 if-else ......
毫不夸张的说,我们都写过类似的代码,回想起被 if-else
支配的恐惧,我们常常无所下手,甚至不了了之。
下面分享一下我在开发中遇到复杂的 if-else
语句“优雅处理”
思路。如有不妥,欢迎大家一起交流学习。
需求
假设有这么一个需求:
一个电商系统,当用户消费满1000
金额,可以根据用户VIP等级,享受打折优惠。
根据用户VIP等级,计算出用户最终的费用。
- 普通会员 不打折
- 白银会员 优惠50元
- 黄金会员 8折
- 白金会员 优惠50元,再打7折
编码实现
private static double getResult(long money, int type) {double result = money;if (money >= 1000) {if (type == UserType.SILVER_VIP.getCode()) {System.out.println("白银会员 优惠50元");result = money - 50;} else if (type == UserType.GOLD_VIP.getCode()) {System.out.println("黄金会员 8折");result = money * 0.8;} else if (type == UserType.PLATINUM_VIP.getCode()) {System.out.println("白金会员 优惠50元,再打7折");result = (money - 50) * 0.7;} else {System.out.println("普通会员 不打折");result = money;}}return result;
}
为了方便演示,代码上我进行了简单实现,但实际上 if - else
会进行复杂的逻辑
计费。 从功能上来说,基本完成,但是对于我这种有代码洁癖的人来说,代码质量上不忍直视。我们开始着手 优化
一下我们的第一版代码
吧。
思考
看到如上代码,聪明的朋友首先想到的是,这不是典型的策略模式
吗?
你可真是个机灵鬼,我们先尝试用策略模式来优化一下代码吧。
策略模式
什么是策略模式?
可能有的朋友还不清楚,什么是策略模式。策略模式是定义一系列的算法,把它们一个个封装
起来, 并且使它们可相互替换
。
比如上述需求,有返利
、有打折
、有折上折
等等。这些算法本身就是一种策略
。并且这些算法可以相互替换
的,比如今天我想让 白银会员优惠50
,明天可以替换为 白银会员打9折
。
说了那么多,不如编码来得实在。
编码
public interface Strategy {// 计费方法double compute(long money);
}// 普通会员策略
public class OrdinaryStrategy implements Strategy {@Overridepublic double compute(long money) {System.out.println("普通会员 不打折");return money;}
}// 白银会员策略
public class SilverStrategy implements Strategy {@Overridepublic double compute(long money) {System.out.println("白银会员 优惠50元");return money - 50;}
}// 黄金会员策略
public class GoldStrategy implements Strategy{@Overridepublic double compute(long money) {System.out.println("黄金会员 8折");return money * 0.8;}
}// 白金会员策略
public class PlatinumStrategy implements Strategy {@Overridepublic double compute(long money) {System.out.println("白金会员 优惠50元,再打7折");return (money - 50) * 0.7;}
}
我们定义来一个 Strategy
接口,并且定义 四个子类,实现接口。在对应的 compute
方法 实现自身策略的计费逻辑。
private static double getResult(long money, int type) {double result = money;if (money >= 1000) {if (type == UserType.SILVER_VIP.getCode()) {result = new SilverStrategy().compute(money);} else if (type == UserType.GOLD_VIP.getCode()) {result = new GoldStrategy().compute(money);} else if (type == UserType.PLATINUM_VIP.getCode()) {result = new PlatinumStrategy().compute(money);} else {result = new OrdinaryStrategy().compute(money);}}return result;
}
然后对应 getResult
方法,根据 type
替换为对应的 用户VIP 策略
。 这里代码上出现了重复的调用 compute
,我们可以尝试进一步优化。
private static double getResult(long money, int type) {if (money < 1000) {return money;}Strategy strategy;if (type == UserType.SILVER_VIP.getCode()) {strategy = new SilverStrategy();} else if (type == UserType.GOLD_VIP.getCode()) {strategy = new GoldStrategy();} else if (type == UserType.PLATINUM_VIP.getCode()) {strategy = new PlatinumStrategy();} else {strategy = new OrdinaryStrategy();}return strategy.compute(money);
}
还记得我说到的卫语句
吗? 我们在这里把 money < 1000 的情况提前 return。更关注于满1000逻辑
,也可以减少不必要的缩进。
深思
我曾一度 以为 策略模式不过如此。以为代码优化到这已经可以了。
但是还有一个恐怖的事情,if-else
依然存在 :)
我尝试翻阅了许多书籍,查看如何消除 策略模式中的 if-else
书中大部分的方法是,使用简单工厂 + 策略模式。把 if - else
切换为 switch
创建一个工厂方法而已。
但是这远远没有达到我想要的效果,打倒 if - else
直到某一天夜里,我大佬在群里分享一个 Java8
小技巧时,从此大开新世界。
工厂 + 策略
public interface Strategy {double compute(long money);// 返回 typeint getType();
}public class OrdinaryStrategy implements Strategy {@Overridepublic double compute(long money) {System.out.println("普通会员 不打折");return money;}// 添加 type 返回@Overridepublic int getType() {return UserType.SILVER_VIP.getCode();}
}public class SilverStrategy implements Strategy {@Overridepublic double compute(long money) {System.out.println("白银会员 优惠50元");return money - 50;}// type 返回@Overridepublic int getType() {return UserType.SILVER_VIP.getCode();}
}....省略剩下 Strategy
我们先在 Strategy 新增一个 getType
方法,用来标示
该策略的 type
值。代码相对简单,这里就不过多介绍了
public class StrategyFactory {private Map<Integer, Strategy> map;public StrategyFactory() {List<Strategy> strategies = new ArrayList<>();strategies.add(new OrdinaryStrategy());strategies.add(new SilverStrategy());strategies.add(new GoldStrategy());strategies.add(new PlatinumStrategy());strategies.add(new PlatinumStrategy());// 看这里 看这里 看这里!map = strategies.stream().collect(Collectors.toMap(Strategy::getType, strategy -> strategy));/* 等同上面map = new HashMap<>();for (Strategy strategy : strategies) {map.put(strategy.getType(), strategy);}*/}public static class Holder {public static StrategyFactory instance = new StrategyFactory();}public static StrategyFactory getInstance() {return Holder.instance;}public Strategy get(Integer type) {return map.get(type);}
}
静态内部类单例,单例模式实现的一种,不是本文重点,如不了解,可以自行
我们再着手创建一个 StrategyFactory
工厂类。StrategyFactory 这里我使用的是静态内部类单例
,在构造方法的时候,初始化好 需要的 Strategy
,并把 list
转化为 map
。 这里 转化就是“灵魂”
所在。
toMap
我们先来看看 Java8
语法中的小技巧。
通常情况下,我们遍历 List,手动put
到 Map 中。
-------------- before -----------------map = new HashMap<>();
for (Strategy strategy : strategies) {map.put(strategy.getType(), strategy);
}-------------- after Java8 -----------------map = strategies.stream().collect(Collectors.toMap(Strategy::getType, strategy -> strategy));
toMap
第一个参数是一个Function,对应 Map 中的 key
,第二个参数也是一个Function,strategy -> strategy, 左边strategy
是遍历 strategies 中的每一个strategy,右边strategy
则是 Map 对应 value
值。
若是不了解
Java8
语法的朋友,强烈建议看 《Java8 实战
》,书中详细的介绍了Lambda
表达式、Stream
等语法。
效果
private static double getResult(long money, int type) {if (money < 1000) {return money;}Strategy strategy = StrategyFactory.getInstance().get(type);if (strategy == null){throw new IllegalArgumentException("please input right type");}return strategy.compute(money);
}
至此,通过一个工厂类,在我们在 getResult()
调用的时候,根据传入 type
,即可获取到 对应 Strategy
再也没有可怕的 if-else
语句。
完结撒花撒花 : )
后续
后续代码优化上,若是 Java 项目,可以尝试使用自定义注解
,注解 Strategy 实现类。
这样可以简化原来需在工厂类 List 添加一个 Stratey 策略
。
链接:https://juejin.im/post/5def654f51882512302daeef
避免过多if - else的新姿势:策略模式、工厂 + 策略相关推荐
- 策略模式(策略设计模式)详解
策略模式(策略设计模式)详解 在现实生活中常常遇到实现某种目标存在多种策略可供选择的情况,例如,出行旅游可以乘坐飞机.乘坐火车.骑自行车或自己开私家车等,超市促销可以釆用打折.送商品.送积分等方法. ...
- 策略模式、策略模式与Spring的碰撞
策略模式是GoF23种设计模式中比较简单的了,也是常用的设计模式之一,今天我们就来看看策略模式. 实际案例 我工作第三年的时候,重构旅游路线的机票查询模块,旅游路线分为四种情况: 如果A地-B地往返都 ...
- 设计模式之策略模式+工厂模式+模板模式结合
设计模式之策略模式+模板模式 为什么总是学不好设计模式 从"登录功能"中发现问题. 首先我们简单的了解功能需求: 于是你开始干活了: 1.控制层代码如下,根据不同的登录方式调用不同 ...
- Java设计模式之策略模式+工厂模式+模板模式
Java设计模式之策略模式+工厂模式+模板模式 1.策略模式+工厂模式+模板模式 个人的理解:实际开发工程中,一些业务很复杂的逻辑使用很多的 if 或者 if···else 语句,不利于维护和扩展,为 ...
- 如何使用 Spring 实现策略模式+工厂模式
欢迎关注方志朋的博客,回复"666"获面试宝典 一.策略模式 策略模式定义了一组算法,将每个算法都封装起来,并且使它们之间可以互换 1.策略模式主要角色 主要角色如下: 封装角色( ...
- 实践:使用Spring 原生注解来快速实现 策略模式 + 工厂模式
作者:Richard_Yi juejin.im/post/5db0e910518825648f2ef355 前言 这阵子在做项目组重构的工作,工作中的一部分就是就目前代码库中与企业交互的逻辑抽离出来, ...
- 策略模式+工厂模式(反射)+枚举代替 大量 if..else if..
实际项目中我们经常碰到需要使用if-else-if的分支判断这种情况. 这种写法带来一些弊端. 一旦分支多太多,逻辑复杂,会导致代码十分冗长,增加阅读难度. 如果需要增加或减少分支,需要改动if-el ...
- 常用设计模式-策略模式+工厂模式+模板模式(使用场景、解决方案)
在策略模式+工厂模式中,没有使用到模板模式,因为张三和李四的业务逻辑都是调用AAA方法,如果现在在增加一个方法,次方法只需要李四一人去实现BBB方法,此时张三的handel中就会报错,需要张三也去实现 ...
- java设计模式之策略模式+工厂模式(优化if-else)
案例:假如以快餐类为需求.模拟现实业务 方法一:简单工厂+策略模式(采用afterPropertiesSet方法) //从这里可以看出使用了大量il/else,如果是真实业务//可能每个if/else ...
最新文章
- RISC-V与DSA计算机架构
- 52 介绍几个重要的类
- Uva 507 - Jill Rides Again(最大子数组求和问题)
- IplImage简介
- 蓝桥杯python青少年_蓝桥杯大赛青少年组省赛结果公布
- confluencejira集成_集成confluence与jira
- Matlab:成功解决The option is not valid. The options must be'double','native','default','omitnan', or'inc
- typescript的命名空间
- 《Head First设计模式》第八章笔记-模板方法模式
- PHP网站后台角色权限管理系统源码
- 某品牌电批单机知识总结
- 学python自学多久_python自学要多久能学会
- Unity3D之FingerGestures使用
- 目标识别与跟踪算法matlab_极市直播| 朱政:基于孪生网络结构的SiamRPN系列目标跟踪算法...
- 计算机cpu的定义,CPU是什么?
- 认识oracle的update更新
- 计算机新教师培训自我评价,教师个人自我评价(精选多篇)
- 欸,自娱自乐的学习必然是缓慢的
- Oracle 修改字符集(AL32UTF8 转换成UTF8字符集)
- 好用的数据恢复软件,误删不再怕
热门文章
- SPSS Modeler ADP自动数据准备学习笔记
- 百度飞桨七日深度学习手势识别
- 矩形图片怎么转换成圆形的图片
- 百万级数据量,千万级数据量是多少,海量数据的优化方案
- 遇见OFFER,阿里云最强技术团队现身招聘,“职”为你来
- 「PyTorch自然语言处理系列」7. 自然语言处理的进阶序列模型
- android generated java files,Android protobuf-javalite 实践
- 成都市计算机会考考试题,成都市初中信息技术会考试题_第七套.docx
- python新手入门程序——实验5
- android 4.4 短信拦截,Android 4.4 KitKat升级率已经接近18%