解决ifelse过多问题

Posted hymKing

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了解决ifelse过多问题相关的知识,希望对你有一定的参考价值。

问题

if else过多问题可以分成三类问题:

  • if…else 过多
  • 逻辑表达式复杂
  • 嵌套过深

本节只讨论过多问题:

public void test()
    if(condition1)
      //do something
    else if(conditon2)
      //do something
    else if(conditon3)
      //do something
    else if(conditon4)
      //do something
    else if(conditon5)
      //do something
    

解决问题

解决if…else过多问题的几种方案:

表驱动、责任链模式、注解驱动、事件驱动、有限状态机、Optional、多态。

结构化思维导图:

方法1:表驱动

**介绍:**对于逻辑表达模式固定的 if…else 代码,可以通过某种映射关系,将逻辑表达式用表格的方式表示;再使用表格查找的方式,找到某个输入所对应的处理函数,使用这个处理函数进行运算。

**适用场景:**逻辑表达式比较固定的场景

使用表驱动法的两个问题:

  1. 怎样从表中查询条目:直接访问、索引访问、阶梯访问
  2. 表里应该存储什么:数据、动作、函数、类,其实都可以,针对于java语言,一切皆是对象,那么表里可以存对象,只是要确定这个对象充当的角色就可以。

直接访问-案例代码:

int getMonthsDay(int month)
    int day;
    switch(month)
        case 1,3,5,7,8,10,12:
            day=31;break;
        case 4,6,9,11:
            day=30;break;
        case 2:
            day=28;break;
            default: day=0;
    
    return  day;



//优化版本表驱动法
int days[]=31,28,31,30,31,30,31,31,30,31,30,31;
int getMonthsDay2(int month)
        return days[month-1];

索引访问-案例代码:

//表驱动法
String getResult(String param) 
    if ("1".equals(param)) 
        return "A";
     else if ("2".equals(param)) 
        return "B";
     else if ("2".equals(param)) 
        return "C";
     else if ("2".equals(param)) 
        return "D";
     else if ("2".equals(param)) 
        return "E";
     else if ("2".equals(param)) 
        return "F";
     else 
        return "g";
    


//优化版本:表驱动法
public static String paramToValue(String param) 
    Map<String,String> table = new HashMap<>();
    table.put("1","A");
    table.put("2","B");
    table.put("3","C");
    table.put("4","D");
    table.put("5","E");
    table.put("6","F");

    return table.get(param);


String getResuet2(String param)
    return paramToValue(param);

阶梯访问-案例代码:

int grades[] = 59,79,84,89,94,100;
String [] level = "F","E","D","C","B","A";
public String getLevel (int grade)
    for( int i = 0 ; i < grades.length ; i++)
      //在一定的阶梯范围内执行
        if(grade <= grades[i])
            return  level[i];
        
    
    return  null;

另外,在java8中引入了lumbda表达式,在实际的表驱动编程模型编码中,使用lumbda表达式,也可以使得代码更加简洁。

总结:表驱动实现的意义有以下三点:

  • 逻辑与数据分离

  • 逻辑修改成本巨大,数据修改成本极小

  • 数据来源灵活,数据改变灵活

方法二:责任链模式

**介绍:**当if…else中的条件表达式灵活多变,无法将条件中的数据抽象为表格并用统一的方式进行判断时,这时应将条件判断权交给每个功能组件。并用链的形式将这些组件串联起来,形成完整的功能。

**适用场景:**条件表达式灵活多变,没有统一的形式。

责任链模式来解决复杂条件表达式,且分支逻辑多的代码块,还是比较好的,这里面就不列举案例,责任链模式是常用的设计模式的一种,在设计模式模块,也会单独分析,有兴趣的可以研究一下。

注:责任链的控制模式

职责链模式在具体实现过程中,会有一些不同的形式。从链的调用控制角度看,可分为外部控制和内部控制两种。

外部控制不灵活,但是减少了实现难度。职责链上某一环上的具体实现不用考虑对下一环的调用,因为外部统一控制了。但是一般的外部控制也不能实现嵌套调用。如果有嵌套调用,并且希望由外部控制职责链的调用,实现起来会稍微复杂。具体可以参考 Spring Web Interceptor 机制的实现方法。

内部控制就比较灵活,可以由具体的实现来决定是否需要调用链上的下一环。但如果调用控制模式是固定的,那这样的实现对于使用者来说是不便的。

方法三:注解驱动

介绍

通过 Java 注解(或其它语言的类似机制)定义执行某个方法的条件。在程序执行时,通过对比入参与注解中定义的条件是否匹配,再决定是否调用此方法。具体实现时,可以采用表驱动或职责链的方式实现。

适用场景

适合条件分支很多多,对程序扩展性和易用性均有较高要求的场景。通常是某个系统中经常遇到新需求的核心功能。

实现和实例

很多框架中都能看到这种模式的使用,比如常见的 Spring MVC。因为这些框架很常用,demo 随处可见,所以这里不再上具体的演示代码了。

这个模式的重点在于实现。现有的框架都是用于实现某一特定领域的功能,例如 MVC。故业务系统如采用此模式需自行实现相关核心功能。主要会涉及反射、职责链等技术。具体的实现这里就不做演示了。

贴了一段通过codota查询出来的代码,是springBoot框架中 @Conditionalxxx系列的注解

@Data
@ConditionalOnExpression("!'$webhook'.isEmpty()")
public class MonitorDingTalkPropertiesConfig 
    private Boolean enabled;

方法四:事件驱动

通过关联不同的事件类型和对应的处理机制,来实现复杂的逻辑,同时达到解耦的目的。

适用场景:

从理论角度讲,事件驱动可以看做是表驱动的一种,但从实践角度讲,事件驱动和前面提到的表驱动有多处不同。具体来说:

  1. 表驱动通常是一对一的关系;事件驱动通常是一对多;
  2. 表驱动中,触发和执行通常是强依赖;事件驱动中,触发和执行是弱依赖(通过观察者模式实现,是一种抽象耦合方式)

实现和实例:

实现方式上,单机的实践驱动可以使用 Guava、Spring 等框架实现,在android中也有EventBus、RxBus之类的事件框架。事件处理方法,接受到不同的事件触发,执行事件处理方法的方法体。

方法五:有限状态机

介绍:

有限状态机通常被称为状态机(无限状态机这个概念可以忽略)。维基百科解释:是表示有限个状态以及在这些状态之间的转移和动作等行为的数学模型。

其实,状态机也可以看做是表驱动的一种,是当前状态和事件两者组合与处理函数的一种对应关系。当然处理成功之后,还有一个状态转移的过程和结果。

使用场景:

业务中,比如订单处理、oa流程、然后在一些组件设计中,比如播放器组件中的play、resume、stop等,只要存在着这种状态和状态的流转,大多可以使用状态机模式进行设计。

实现和实例:

比如android sdk中的MediaPlayer组件的设计。这里面附上一张,Andorid developer 官方网站,关于MediaPlayer的设计状态图

方法六:Optional

介绍:

Java 代码中的一部分 if…else 是由非空检查导致的。因此,降低这部分带来的 if…else 也就能降低整体的 if…else 的个数。

Java 从 8 开始引入了 Optional 类,用于表示可能为空的对象。这个类提供了很多方法,用于相关的操作,可以用于消除 if…else。开源框架 Guava 和 Scala 语言也提供了类似的功能。

使用场景:

有较多的用于非空判断的if…else。

实现和实例:

public void  optionalTest(int numbler)
    Optional<Integer> stringOpt= Optional.of(numbler);
    // Optional.orElse - 如果值存在,返回它,否则返回默认值
    Integer value1 = stringOpt.orElse(new Integer(0));

通过optional的orElse的api,替换了一个if…else的逻辑。java 8中引入了Optional类,另外在Java9中对Optional类,又继续做了加强,提供了比如 ifPresentOrElse的新api,都可以用来简化我们的非空判断的if…else的操作。

方法七:多态

介绍:

多态是同一个行为具有多个不同表现形式或形态的能力。在实现上来讲,多态就是同一个接口,使用不同的实例而执行不同的操作。

多态存在的三个必要条件:继承重写父类引用指向子类对象(耦合关系会由普通耦合转变成抽象耦合)。

只有多态实现,并不能解决if…else过多问题,还需要结合表驱动法,来消除if…else过问题。

使用场景:

类似于策略类的场景,可以使用这种方式,进行优化。

实现和实例:【重构改善既有的代码设计】

package com.hym.ifopt;

public class DuoTaiMain 

    public static void main(String[] args) 
        //多态实现后的调用
        
    
    //此部分是ifelse的实现方式//
    String shapeType="";

    enum SHAPE CIRCLE,RECTANGLE,SQUARE;
    /**求面积的api*/
    double getArea(double param1,double param2)
        if(shapeType.equals(SHAPE.CIRCLE))
            return 3.14*param1*param2;
        else if (shapeType.equals(SHAPE.RECTANGLE))
            return param1*param2;
        else if(shapeType.equals(SHAPE.SQUARE))
            return param1*param1;
        
        return -1;
    

    转换成多态的方式进行优化//
    interface Shape 
        double getArea(double param1, double param2);
    
    class Circle implements Shape 
        @Override
        public double getArea(double param1, double param2) 
             return 3.14*param1*param2;
        
    

    class Rectangle implements Shape 

        @Override
        public double getArea(double param1, double param2) 
            return param1*param2;
        
    

    class Square implements Shape
        @Override
        public double getArea(double param1, double param2) 
            return param1*param1;
        
    
    /**多态方式的调用函数*/
    double getArea2(Shape shape)
       return shape.getArea(1,2);
    

在《重构改善既有代码的设计》一书中对简化条件表达式,提出了8点建议,会在简化条件表达式章节继续研究阐述。
个人公号:Hym4Android
参考链接:

https://www.runoob.com/java/java8-lambda-expressions.html

以上是关于解决ifelse过多问题的主要内容,如果未能解决你的问题,请参考以下文章

算法创作|阶梯电价问题解决方法

9个小技巧让你的 if else看起来更优雅

9个小技巧让你的 if else看起来更优雅

阶梯问题

策略模式优化过多的IF ELSE

项目中阶梯费率解决方法,数组中通过键名查找键值