目录

前言

需求

编码实现

思考

策略模式

什么是策略模式?

编码

深思

工厂 + 策略

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);}
}

静态内部类单例,单例模式实现的一种,不是本文重点,如不了解,可以自行 google

我们再着手创建一个 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的新姿势:策略模式、工厂 + 策略相关推荐

  1. 策略模式(策略设计模式)详解

    策略模式(策略设计模式)详解 在现实生活中常常遇到实现某种目标存在多种策略可供选择的情况,例如,出行旅游可以乘坐飞机.乘坐火车.骑自行车或自己开私家车等,超市促销可以釆用打折.送商品.送积分等方法. ...

  2. 策略模式、策略模式与Spring的碰撞

    策略模式是GoF23种设计模式中比较简单的了,也是常用的设计模式之一,今天我们就来看看策略模式. 实际案例 我工作第三年的时候,重构旅游路线的机票查询模块,旅游路线分为四种情况: 如果A地-B地往返都 ...

  3. 设计模式之策略模式+工厂模式+模板模式结合

    设计模式之策略模式+模板模式 为什么总是学不好设计模式 从"登录功能"中发现问题. 首先我们简单的了解功能需求: 于是你开始干活了: 1.控制层代码如下,根据不同的登录方式调用不同 ...

  4. Java设计模式之策略模式+工厂模式+模板模式

    Java设计模式之策略模式+工厂模式+模板模式 1.策略模式+工厂模式+模板模式 个人的理解:实际开发工程中,一些业务很复杂的逻辑使用很多的 if 或者 if···else 语句,不利于维护和扩展,为 ...

  5. 如何使用 Spring 实现策略模式+工厂模式

    欢迎关注方志朋的博客,回复"666"获面试宝典 一.策略模式 策略模式定义了一组算法,将每个算法都封装起来,并且使它们之间可以互换 1.策略模式主要角色 主要角色如下: 封装角色( ...

  6. 实践:使用Spring 原生注解来快速实现 策略模式 + 工厂模式

    作者:Richard_Yi juejin.im/post/5db0e910518825648f2ef355 前言 这阵子在做项目组重构的工作,工作中的一部分就是就目前代码库中与企业交互的逻辑抽离出来, ...

  7. 策略模式+工厂模式(反射)+枚举代替 大量 if..else if..

    实际项目中我们经常碰到需要使用if-else-if的分支判断这种情况. 这种写法带来一些弊端. 一旦分支多太多,逻辑复杂,会导致代码十分冗长,增加阅读难度. 如果需要增加或减少分支,需要改动if-el ...

  8. 常用设计模式-策略模式+工厂模式+模板模式(使用场景、解决方案)

    在策略模式+工厂模式中,没有使用到模板模式,因为张三和李四的业务逻辑都是调用AAA方法,如果现在在增加一个方法,次方法只需要李四一人去实现BBB方法,此时张三的handel中就会报错,需要张三也去实现 ...

  9. java设计模式之策略模式+工厂模式(优化if-else)

    案例:假如以快餐类为需求.模拟现实业务 方法一:简单工厂+策略模式(采用afterPropertiesSet方法) //从这里可以看出使用了大量il/else,如果是真实业务//可能每个if/else ...

最新文章

  1. RISC-V与DSA计算机架构
  2. 52 介绍几个重要的类
  3. Uva 507 - Jill Rides Again(最大子数组求和问题)
  4. IplImage简介
  5. 蓝桥杯python青少年_蓝桥杯大赛青少年组省赛结果公布
  6. confluencejira集成_集成confluence与jira
  7. Matlab:成功解决The option is not valid. The options must be'double','native','default','omitnan', or'inc
  8. typescript的命名空间
  9. 《Head First设计模式》第八章笔记-模板方法模式
  10. PHP网站后台角色权限管理系统源码
  11. 某品牌电批单机知识总结
  12. 学python自学多久_python自学要多久能学会
  13. Unity3D之FingerGestures使用
  14. 目标识别与跟踪算法matlab_极市直播| 朱政:基于孪生网络结构的SiamRPN系列目标跟踪算法...
  15. 计算机cpu的定义,CPU是什么?
  16. 认识oracle的update更新
  17. 计算机新教师培训自我评价,教师个人自我评价(精选多篇)
  18. 欸,自娱自乐的学习必然是缓慢的
  19. Oracle 修改字符集(AL32UTF8 转换成UTF8字符集)
  20. 好用的数据恢复软件,误删不再怕

热门文章

  1. SPSS Modeler ADP自动数据准备学习笔记
  2. 百度飞桨七日深度学习手势识别
  3. 矩形图片怎么转换成圆形的图片
  4. 百万级数据量,千万级数据量是多少,海量数据的优化方案
  5. 遇见OFFER,阿里云最强技术团队现身招聘,“职”为你来
  6. 「PyTorch自然语言处理系列」7. 自然语言处理的进阶序列模型
  7. android generated java files,Android protobuf-javalite 实践
  8. 成都市计算机会考考试题,成都市初中信息技术会考试题_第七套.docx
  9. python新手入门程序——实验5
  10. android 4.4 短信拦截,Android 4.4 KitKat升级率已经接近18%