设计模式第八讲:常见重构技巧 - 去除多余的if else

这篇具有很好参考价值的文章主要介绍了设计模式第八讲:常见重构技巧 - 去除多余的if else。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

设计模式第八讲:常见重构技巧 - 去除多余的if else

最为常见的是代码中使用很多的if/else,或者switch/case;如何重构呢?方法特别多,本文是设计模式第八讲,带你学习其中的技巧。

1、出现if/else和switch/case的场景

通常业务代码会包含这样的逻辑:每种条件下会有不同的处理逻辑。比如两个数a和b之间可以通过不同的操作符(+,-,*,/)进行计算,初学者通常会这么写:

public int calculate(int a, int b, String operator) {
    int result = Integer.MIN_VALUE;
 
    if ("add".equals(operator)) {
        result = a + b;
    } else if ("multiply".equals(operator)) {
        result = a * b;
    } else if ("divide".equals(operator)) {
        result = a / b;
    } else if ("subtract".equals(operator)) {
        result = a - b;
    }
    return result;
}

或者用switch/case:

public int calculateUsingSwitch(int a, int b, String operator) {
    switch (operator) {
    case "add":
        result = a + b;
        break;
    // other cases    
    }
    return result;
}

这种最基础的代码如何重构呢?

2、重构思路

有非常多的重构方法来解决这个问题, 这里会列举很多方法,在实际应用中可能会根据场景进行一些调整;另外不要纠结这些例子中显而易见的缺陷(比如没用常量,没考虑多线程等等),而是把重心放在学习其中的思路上。

2.1、方式一 - 工厂类

工厂设计模式可以参考这篇文章:JAVA设计模式第二讲:创建型设计模式

  • 第8.2节

  • 定义一个操作接口

public interface Operation {
    int apply(int a, int b);
}
  • 实现操作, 这里只以add为例
public class Addition implements Operation {
    @Override
    public int apply(int a, int b) {
        return a + b;
    }
}
  • 实现操作工厂
public class OperatorFactory {
    static Map<String, Operation> operationMap = new HashMap<>();
    static {
        operationMap.put("add", new Addition());
        operationMap.put("divide", new Division());
        // more operators
    }
 
    public static Optional<Operation> getOperation(String operator) {
        return Optional.ofNullable(operationMap.get(operator));
    }
}
  • 在Calculator中调用
public int calculateUsingFactory(int a, int b, String operator) {
    Operation targetOperation = OperatorFactory
      .getOperation(operator)
      .orElseThrow(() -> new IllegalArgumentException("Invalid Operator"));
    return targetOperation.apply(a, b);
}

对于上面为什么方法名是apply,Optional怎么用? 请参考这篇:

  • java8/Stream流式计算从入门到精通/函数式编程实战
    • Lambda 表达式的特点?
    • Lambda 表达式使用和Stream下的接口?
    • 函数接口定义和使用,四大内置函数接口Consumer,Function,Supplier, Predicate.
    • Comparator排序为例贯穿所有知识点。
  • Java8特性第三讲:如何使用Optional类优雅解决业务npe问题
    • Optional类的意义?
    • Optional类有哪些常用的方法?
    • Optional举例贯穿所有知识点
    • 如何解决多重类嵌套Null值判断?

2.2、方式二 - 枚举

枚举适合类型固定,可枚举的情况,比如如下操作符;同时枚举中是可以提供方法实现的,这就是我们可以通过枚举进行重构的原因。

  • 定义操作符枚举
public enum Operator {
    ADD {
        @Override
        public int apply(int a, int b) {
            return a + b;
        }
    },
    // other operators
    
    public abstract int apply(int a, int b);

}
  • 在Calculator中调用
public int calculate(int a, int b, Operator operator) {
    return operator.apply(a, b);
}
  • 写个测试用例测试下:
@Test
public void whenCalculateUsingEnumOperator_thenReturnCorrectResult() {
    Calculator calculator = new Calculator();
    int result = calculator.calculate(3, 4, Operator.valueOf("ADD"));
    assertEquals(7, result);
}

看是否很简单?

2.3、方法三 - 命令模式

命令模式也是非常常用的重构方式,把每个操作符当作一个Command。

  • 首先让我们回顾下什么是命令模式

    • 看这篇文章:JAVA设计模式第四讲:行为型设计模式

      • 第10.8节
    • 命令模式(Command pattern):将"请求"封闭成对象,以便使用不同的请求、队列或者日志来参数化其他对象。命令模式也支持可撤销的操作。

      • Command: 命令
      • Receiver: 命令接收者,也就是命令真正的执行者
      • Invoker: 通过它来调用命令
      • Client: 可以设置命令与命令的接收者

      设计模式第八讲:常见重构技巧 - 去除多余的if else,Java 设计模式详解,设计模式,重构,工厂模式,命令模式,规则引擎,策略模式

  • Command接口

public interface Command {
    Integer execute();
}
  • 实现Command
public class AddCommand implements Command {
    // Instance variables
 
    public AddCommand(int a, int b) {
        this.a = a;
        this.b = b;
    }
 
    @Override
    public Integer execute() {
        return a + b;
    }
}
  • 在Calculator中调用
public int calculate(Command command) {
    return command.execute();
}
  • 测试用例
@Test
public void whenCalculateUsingCommand_thenReturnCorrectResult() {
    Calculator calculator = new Calculator();
    int result = calculator.calculate(new AddCommand(3, 7));
    assertEquals(10, result);
}

注意,这里new AddCommand(3, 7)仍然没有解决动态获取操作符问题,所以通常来说可以结合简单工厂模式来调用:

  • 创建型 - 简单工厂(Simple Factory)
    • 简单工厂(Simple Factory),它把实例化的操作单独放到一个类中,这个类就成为简单工厂类,让简单工厂类来决定应该用哪个具体子类来实例化,这样做能把客户类和具体子类的实现解耦,客户类不再需要知道有哪些子类以及应当实例化哪个子类

2.4、方法四 - 规则引擎

规则引擎适合规则很多且可能动态变化的情况,在先要搞清楚一点Java OOP,即类的抽象:

  • 这里可以抽象出哪些类?// 头脑中需要有这种自动转化
    • 规则Rule
      • 规则接口
      • 具体规则的泛化实现
    • 表达式Expression
      • 操作符
      • 操作数
    • 规则引擎
  • 定义规则
public interface Rule {
    boolean evaluate(Expression expression);
    Result getResult();
}
  • Add 规则
public class AddRule implements Rule {
    @Override
    public boolean evaluate(Expression expression) {
        boolean evalResult = false;
        if (expression.getOperator() == Operator.ADD) {
            this.result = expression.getX() + expression.getY();
            evalResult = true;
        }
        return evalResult;
    }    
  	@Override
    public Result getResult() {
       ...
    }
}
  • 表达式
public class Expression {
    private Integer x;
    private Integer y;
    private Operator operator;        
}
  • 规则引擎
public class RuleEngine {
    private static List<Rule> rules = new ArrayList<>();
 
    static {
        rules.add(new AddRule());
    }
 
    public Result process(Expression expression) {
        Rule rule = rules
          .stream()
          .filter(r -> r.evaluate(expression))
          .findFirst()
          .orElseThrow(() -> new IllegalArgumentException("Expression does not matches any Rule"));
        return rule.getResult();
    }
}
  • 测试用例
@Test
public void whenNumbersGivenToRuleEngine_thenReturnCorrectResult() {
    Expression expression = new Expression(5, 5, Operator.ADD);
    RuleEngine engine = new RuleEngine();
    Result result = engine.process(expression);
 
    assertNotNull(result);
    assertEquals(10, result.getValue());
}

2.5、方法五 - 策略模式

策略模式比命令模式更为常用,而且在实际业务逻辑开发中需要注入一定的(比如通过Spring的@Autowired来注入bean),这时通过策略模式可以巧妙的重构

  • 什么是策略模式?

    • 我们再复习下:JAVA设计模式第四讲:行为型设计模式
      • 第10.3节
    • 策略模式(strategy pattern):定义了算法族,分别封闭起来,让它们之间可以互相替换,此模式让算法的变化独立于使用算法的客户
      • Strategy 接口定义了一个算法族,它们都具有 behavior() 方法。
      • Context 是使用到该算法族的类,其中的 doSomething() 方法会调用 behavior(),setStrategy(in Strategy) 方法可以动态地改变 strategy 对象,也就是说能动态地改变 Context 所使用的算法。

    设计模式第八讲:常见重构技巧 - 去除多余的if else,Java 设计模式详解,设计模式,重构,工厂模式,命令模式,规则引擎,策略模式

  • Spring中需要注入资源重构?

如果是在实现业务逻辑需要注入框架中资源呢?比如通过Spring的@Autowired来注入bean。可以这样实现:

  • 操作 // 很巧妙
public interface Opt {
    int apply(int a, int b);
}

@Component(value = "addOpt")
public class AddOpt implements Opt {
  	// 这里通过Spring框架注入了资源
    @Autowired
    xxxAddResource resource; 

    @Override
    public int apply(int a, int b) {
       return resource.process(a, b);
    }
}

@Component(value = "devideOpt")
public class devideOpt implements Opt {
  	// 这里通过Spring框架注入了资源
    @Autowired
    xxxDivResource resource; 

    @Override
    public int apply(int a, int b) {
       return resource.process(a, b);
    }
}
  • 策略
@Component
public class OptStrategyContext{
 

    private Map<String, Opt> strategyMap = new ConcurrentHashMap<>();
 
    @Autowired
    public OptStrategyContext(Map<String, Opt> strategyMap) {
        this.strategyMap.clear();
        this.strategyMap.putAll(strategyMap);
    }
 
    public int apply(Sting opt, int a, int b) {
        return strategyMap.get(opt).apply(a, b);
    }
}

上述代码在实现中非常常见。

3、一些反思

最怕的是刚学会成语,就什么地方都想用成语。文章来源地址https://www.toymoban.com/news/detail-686274.html

  • 真的要这么重构吗?
    • 在实际开发中,切记最怕的是刚学会成语,就什么地方都想用成语; 很多时候不是考虑是否是最佳实现,而是折中(通常是业务和代价的折中,开发和维护的折中…),在适当的时候做适当的重构
    • 很多时候,让团队可持续性的维护代码便是最佳;
    • 重构后会生成很多类,一个简单业务搞这么复杂?所以你需要权衡

4、参考文章

  • https://www.baeldung.com/java-replace-if-statements

到了这里,关于设计模式第八讲:常见重构技巧 - 去除多余的if else的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 《微服务架构设计模式》第十三章 微服务架构的重构策略

    1、为什么重构 单体地狱造成的业务问题: 交付缓慢 充满故障的软件交付 可扩展性差 2、重构形式 1、一步到位 你企图从零开始开发一个全新的基于微服务的应用程序(彻底替换遗留的单体应用)。虽然从头开始并抛弃老代码库听起来很有吸引力,但它的风险极高,很可能以

    2024年02月16日
    浏览(32)
  • 【Java基础教程】(三十八)常用类库篇 · 第八讲:数组操作类——解析Arrays类中的全部操作方法,解锁Java数组操作技巧~

    前言:在学习本文之前,应该先学习并清楚了解Java基础部分的数组相关的概念和知识。 若还不具备学习条件,请先前往学习数组部分的基础知识: 《【Java基础教程】(八)面向对象篇 · 第二讲:Java 数组全面解析——动态与静态初始化、二维数组、方法参数传递、排序与转

    2024年02月15日
    浏览(47)
  • 【设计模式之美】重构(三)之解耦方法论:如何通过封装、抽象、模块化、中间层等解耦代码?

    重构可以分为大规模高层重构(简称“大型重构”)和小规模低层次重构(简称“小型重构”)。 通过解耦对代码重构,就是保证代码不至于复杂到无法控制的有效手段。   代码是否需要“解耦”? 看修改代码会不会牵一发而动全身。 依赖关系是否复杂 把模块与模块之间

    2024年01月16日
    浏览(39)
  • Java设计模式:简介与常见模式

    Java中常见的设计模式有很多,以下是其中一些常见的设计模式及其作用、优势和适用场景: 作用:确保一个类只有一个实例,并提供全局访问点。 优势:节约系统资源,避免多个实例造成的冲突。 适用场景:需要限制类的实例化次数,例如线程池、数据库连接池。 作用:

    2024年02月09日
    浏览(46)
  • C++面试:单例模式、工厂模式等简单的设计模式 & 创建型、结构型、行为型设计模式的应用技巧

            理解和能够实现基本的设计模式是非常重要的。这里,我们将探讨两种常见的设计模式:单例模式和工厂模式,并提供一些面试准备的建议。 目录 单例模式 (Singleton Pattern) 工厂模式 (Factory Pattern) 面试准备  1. 理解设计模式的基本概念 2. 掌握实现细节 3. 讨论优缺

    2024年02月01日
    浏览(55)
  • 常见设计模式记录

        确保某一个类只有一个实例,而且自行实例化并向整个系统提供这 个实例。 使用场景: 要求生成唯一序列号的环境; 在整个项目中需要一个共享访问点或共享数据,例如一个Web页面上的计数 器,可以不用把每次刷新都记录到数据库中,使用单例模式保持计数器的值,

    2024年02月08日
    浏览(41)
  • 前端常见的设计模式

    说到设计模式,大家想到的就是六大原则,23种模式。这么多模式,并非都要记住,但作为前端开发,对于前端出现率高的设计模式还是有必要了解并掌握的,浅浅掌握9种模式后,整理了这份文章。 依赖倒置原则(Dependence Inversion Principle):高层(业务层)不应该直接调用底层(基

    2024年02月19日
    浏览(25)
  • 常见设计模式

    单例模式 单例对象的类必须保证只有一个实例存在,整个系统只能使用一个对象实例,优点:不会频繁地创建和销毁对象,浪费系统资源。缺点是没有抽象层,难以扩展。 单例模式的常见写法: 饿汉式单例模式的写法:线程安全 ,顾名思义,类⼀加载就创建对象,这种⽅式

    2024年02月07日
    浏览(42)
  • 常见的设计模式(模板与方法,观察者模式,策略模式)

    随着时间的推移,软件代码越来越庞大,随着而来的就是如何维护日趋庞大的软件系统。在面向对象开发出现之前,使用的是面向过程开发来设计大型的软件程序,面向过程开发将软件分成一个个单独的模块,模块之间使用函数进行组合,最后完成系统的开发,每次需要修改

    2024年01月22日
    浏览(27)
  • 常见的23种设计模式

    常见的设计模式有23种,它们分别是: 1. 工厂方法模式(Factory Method Pattern) 2. 抽象工厂模式(Abstract Factory Pattern) 3. 单例模式(Singleton Pattern) 4. 建造者模式(Builder Pattern) 5. 原型模式(Prototype Pattern) 6. 适配器模式(Adapter Pattern) 7. 桥接模式(Bridge Pattern) 8. 过滤器模

    2024年02月07日
    浏览(25)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包