消除代码中的if-else

一、代码中常见if-else弊端

if-else 是一种常见的流程控制语句,用于根据条件执行不同的操作。然而,在代码中过多地使用 if-else 分支结构会带来一些弊端,具体包括:

1. 可读性差: 过多的 if-else 分支结构会使代码变得复杂和难以阅读,尤其是嵌套的 if-else 语句,容易引起“深度坍塌”问题。

2. 维护成本高: 当需求发生变化时,需要修改 if-else 分支结构对应的代码分支,如果分支较多,则修改成本相应就会增加。

3. 可扩展性差: 如果需要添加新的分支,就需要在 if-else 分支结构中添加新的代码分支,可能会影响原有代码的逻辑和稳定性。

4. 难以测试: 由于 if-else 分支结构存在多个代码分支,因此很难对所有情况进行全面、有效的测试,可能会出现一些漏洞或错误。
例如一下代码:

public String getGrade(int score) {
    if (score >= 90) {
        return "A";
    } else if (score >= 80) {
        return "B";
    } else if (score >= 70) {
        return "C";
    } else if (score >= 60) {
        return "D";
        
    } else if 
    ...
}

二、策略模式+工厂模式消除

策略模式和工厂模式可以结合起来使用,以消除 if-else 分支结构。具体步骤如下:

  1. 定义一个策略接口,该接口包含所需的方法。

  2. 定义多个实现策略接口的类,每个类实现策略接口中定义的方法,代表着不同的逻辑分支。

  3. 定义一个工厂类,用于根据输入参数返回对应的策略对象。

  4. 在客户端代码中,通过工厂类获取所需的策略对象,并调用其方法完成业务操作。

下面是一个示例代码,该代码使用策略模式和工厂模式消除了 if-else 分支结构:

// 定义策略接口
public interface GradeStrategy {
    String getGrade(int score);
}

// 定义多个实现策略接口的类
public class AGradeStrategy implements GradeStrategy {
    @Override
    public String getGrade(int score) {
        return "A";
    }
}

public class BGradeStrategy implements GradeStrategy {
    @Override
    public String getGrade(int score) {
        return "B";
    }
}

public class CGradeStrategy implements GradeStrategy {
    @Override
    public String getGrade(int score) {
        return "C";
    }
}

public class DGradeStrategy implements GradeStrategy {
    @Override
    public String getGrade(int score) {
        return "D";
    }
}

public class FGradeStrategy implements GradeStrategy {
    @Override
    public String getGrade(int score) {
        return "F";
    }
}

// 定义工厂类
public class GradeStrategyFactory {

    private static final RangeMap<Integer, GradeStrategy> RANGE_MAP = TreeRangeMap.create();

    static {
        RANGE_MAP.put(Range.closed(90, 100), new AGradeStrategy());
        RANGE_MAP.put(Range.closed(80, 89), new BGradeStrategy());
        RANGE_MAP.put(Range.closed(70, 79), new CGradeStrategy());
        RANGE_MAP.put(Range.closed(60, 69), new DGradeStrategy());
        RANGE_MAP.put(Range.closed(0, 59), new FGradeStrategy());
    }

    public static GradeStrategy getGradeStrategy(int score) {
        return RANGE_MAP.get(score);
    }
}

// 在客户端代码中,通过工厂类获取策略对象,并调用其方法完成业务操作
public String getGrade(int score) {
    GradeStrategy gradeStrategy = GradeStrategyFactory.getGradeStrategy(score);
    return gradeStrategy.getGrade(score);
}

在上述代码中,我们首先定义了一个 GradeStrategy 接口,然后定义了五个实现该接口的类,分别代表不同的逻辑分支。接着,我们定义了一个 GradeStrategyFactory 工厂类,用于根据输入参数返回对应的策略对象。在客户端代码中,我们通过工厂类获取所需的策略对象,并调用其方法完成业务操作,从而消除了 if-else 分支结构。

使用策略模式和工厂模式结合起来可以使代码更加灵活、易扩展和可维护,避免了 if-else 分支结构带来的问题。

我们再看一下上述代码UML类图:

三、枚举方法消除

使用枚举来实现接口并消除 if-else 分支结构也是一种很好的方式。以下是使用枚举来实现相同功能的示例代码:

// 定义接口
public interface GradeStrategy {

    String getGrade(int score);

}

// 枚举
public enum GradeEnum implements GradeStrategy {

    A(Range.atLeast(90)) {
        @Override
        public String getGrade(int score) {
            return "A";
        }
    },
    B(Range.closedOpen(80, 90)) {
        @Override
        public String getGrade(int score) {
            return "B";
        }
    },
    C(Range.closedOpen(70, 80)) {
        @Override
        public String getGrade(int score) {
            return "C";
        }
    },
    D(Range.closedOpen(60, 70)) {
        @Override
        public String getGrade(int score) {
            return "D";
        }
    },
    F(Range.lessThan(60)) {
        @Override
        public String getGrade(int score) {
            return "F";
        }
    };


    private final Range<Integer> range;

    GradeEnum(Range<Integer> range) {
        this.range = range;
    }

    public Range<Integer> getRange() {
        return range;
    }

    /**
     * 等级获取
     *
     * @param score
     * @return
     */
    public static String grade(Integer score) {
       return Arrays.stream(GradeEnum.values()).filter(e->e.range.contains(score)).findFirst().get().getGrade(score);
    }
}

这样,我们通过简单的枚举类型定义就可以达到消除 if-else 分支结构的目的,并且代码非常简洁可读。这种方式在处理只有少量分支逻辑的情况下,尤其适合使用。需要注意的是,由于枚举类型是不可变的,因此不存在线程安全问题。

四、Spring服务定位消除

1. 描述

服务定位模式(Service Locator Pattern)是一种经典的设计模式,用于实现组件之间的解耦和松散耦合。该模式将服务的查找和创建过程抽象出来,封装到一个服务定位器中,客户端可以通过服务定位器获取所需的服务实例,而不需要直接依赖于特定的服务实现类。

2. UML类图


我们采用不同数据源的案例来阐述说明

3. 示例代码

数据源接口

public interface DataSource {

    /**
     * 执行更新操作
     * @param sql
     */
    void update(String sql);
}

数据源接口实现类

@Component("MYSQL")// 这里要和枚举保持一致
public class MysqlDataSource implements DataSource {

    @Override
    public void update(String sql) {
        System.out.println("mysql数据库执行更新操作");
    }
}

@Component("ORACLE")// 这里要和枚举保持一致
public class OracleDataSource implements DataSource {

    @Override
    public void update(String sql) {
        System.out.println("oracle执行更新操作");
    }
}

@Component("SQLSERVER")// 这里要和枚举保持一致
public class SqlServerDataSource implements DataSource {

    @Override
    public void update(String sql) {
        System.out.println("sqlserver执行更新操作");
    }
}

数据源配置类

@Configuration
public class DataSourceConfig {

    @Bean("dataSourceFactory")
    public FactoryBean serviceLocatorFactoryBean() {
        ServiceLocatorFactoryBean factoryBean = new ServiceLocatorFactoryBean();
        // 设置服务定位接口
        factoryBean.setServiceLocatorInterface(DataSourceFactory.class);
        return factoryBean;
    }
}

public interface DataSourceFactory {

    /**
     * 获取数据源
     * @param type
     * @return
     */
    DataSource getDataSource(DataSourceEnum type);
}

数据源枚举类

public enum DataSourceEnum {
    MYSQL,
    ORACLE,
    SQLSERVER;
}

客户端

@Component
public class SLFBClient {

    @Autowired
    private DataSourceFactory dataSourceFactory;


    public void update(DataSourceEnum type) {
        dataSourceFactory.getDataSource(type).update("update table set...");
    }
}

测试

@SpringBootTest(classes = SpringBootDesignerApplication.class)
public class AppTest {

    @Resource
    private SLFBClient slfbClient;




    @Test
    public void sLFBClientTest() {
        slfbClient.update(DataSourceEnum.MYSQL);
    }

}

五、 函数式编程消除

函数式编程可以通过 Lambda 表达式和函数式接口来消除 if-else 分支结构,使代码更加简洁、易读和可维护。下面是一个使用函数式编程消除 if-else 分支结构的示例代码:

// 创建函数式接口
public interface GradeFunction {
    String apply(int score);
}

// 客户端测试代码
public class GradeFunctionTest {

    public static String getGrade(int score) {
        RangeMap<Integer, GradeFunction> gradeFunctions = TreeRangeMap.create();
        gradeFunctions.put(Range.atLeast(90), score1 -> "A");
        gradeFunctions.put(Range.closedOpen(80, 90), score1 -> "B");
        gradeFunctions.put(Range.closedOpen(70, 80), score1 -> "C");
        gradeFunctions.put(Range.closedOpen(60, 70), score1 -> "D");
        gradeFunctions.put(Range.lessThan(60), score1 -> "F");
        GradeFunction gradeFunction = gradeFunctions.get(score);
        return gradeFunction != null ? gradeFunction.apply(score) : "";
    }

    public static void main(String[] args) {
        System.out.println(getGrade(30));
    }
}

是不是感觉代码很精简,甚至可以更加简洁,直接去掉自定义函数式接口,具体代码如下:

public class GradeFunctionTest {

    public static String getGrade(int score) {
        RangeMap<Integer, Function<Integer, String>> gradeFunctions = TreeRangeMap.create();
        gradeFunctions.put(Range.atLeast(90), score1 -> "A");
        gradeFunctions.put(Range.closedOpen(80, 90), score1 -> "B");
        gradeFunctions.put(Range.closedOpen(70, 80), score1 -> "C");
        gradeFunctions.put(Range.closedOpen(60, 70), score1 -> "D");
        gradeFunctions.put(Range.lessThan(60), score1 -> "F");
        final Function<Integer, String> integerStringFunction = gradeFunctions.get(score);
        return integerStringFunction != null ? integerStringFunction.apply(score) : "";
    }

    public static void main(String[] args) {
        System.out.println(getGrade(30));
    }
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值