zoukankan      html  css  js  c++  java
  • 怎样避免 i f 判断过多,全复杂度较高,代码不美观的问题?

    没有什么好的设计方式可以实现,减少一个方法中出现几十个 if 匹配的判断?
    现在要做一个判断客户是否通过验证的接口。
    一共有30多个验证规则的判断, 每个规则对应一个规则号;
    这个接口只需要返回是否验证通过 接口参数是一个数组,数组里面放入的是需要验证哪些规则的规则号;

    我想到的比较原始的方法可能是
    /**
    * 验证客户是否通过验证
    * @param rules 需要验证的多条规则的对应码的数组
    /
    public boolean checkPass(int[] rules){
    boolean isPass = true;
    for(int rule:rules){
    if(!isPass){
    break;//前一项不通过直接结束;
    }
    if(1rule){
    isPass = doCheck_1();
    }
    if(2
    rule){
    isPass = doCheck_2();
    }
    if(3==rule){
    isPass = doCheck_3();
    }
    /

    *
    */
    if(39==rule){
    isPass = doCheck_39();
    }
    }
    return isPass;
    }
    但是这样代码写出来圈复杂度肯定超的不知道哪去了。。。

    请教大家有什么方法或是用什么设计模式解决这样的问题比较好?

    changchang 写道

    可以通过map的方式来对这种if-else的情况进行解偶。
    用一个接口把验证操作抽象出来。

    public interface CheckRule {
    public boolean doCheck();
    }

    然后定义具体的验证规则类实现这个接口:

    public class CheckRule1 implements CheckRule {
    public boolean doCheck() {
    //doCheck1
    ...
    }
    }

    在验证服务类初始化的时候,建立起一个规则号到规则类的映射。

    Map<Integer, CheckRule> rules = new HashMap<Integer, CheckRule>();
    rules.put(1, new CheckRule1());

    这工作部分可以放在配置文件(如,spring)中完成,这样以后需要增减修改规则的时候会比较方便。

    最后把checkPass改写一下就可以了。

    public boolean checkPass(int[] ruleCodes){
    for(int ruleCode : ruleCodes) {
    if(!rules.get(ruleCode).doCheck()) {
    return false;
    }
    }
    return true;
    }

    ========================================================================================

    怎么在Java中替换掉繁杂的if语句,高级工程师也会遗漏的知识点
    来一杯82年的Java 2019-01-07 14:20:59

    1. 概述

    决策结构在大多数编程语言中占据了至为重要的一步。但是我们常常会被大量的那种让代码变得难读且难维护的内嵌if语句搞得浑身难受。

    在这次的教程中,我们将来过一下可以代替内嵌if语句的各种方法。让我们来探索简化我们代码的途径吧。

    1. 案例学习

    通常我们会遇到一些需要做一系列条件处理的业务逻辑,并且它们每一个都需要不同的处理。

    为了演示,我们来看一下Calulator(计算器)类的一个例子。上面是带有两个数字类型参数,一个操作符参数以及基于操作的数值返回值的一个方法:

    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语句来操作:

    public int calculateUsingSwitch(int a, int b, String operator) {
    switch (operator) {
    case "add":
    result = a + b;
    break;
    // other cases
    }
    return result;
    }
    在典型的开发过程中,本质上if语句会使程序变得更为臃肿和复杂。并且,switch语句也并非所有场景都适用,当条件复杂的时候,switch语句就没什么作用了。

    另一个使用嵌套条件声明编程的影响是它使得程序变得难以管理。例如,如果我们需要新添加一个操作,我们需要添加一个新的if条件以及条件的实现。

    1. 重构

    让我们尝试下使用其他更简洁和易管理的方法来代替这个复杂的if语句吧。

    3.1 工厂类

    很多时候我们经常遇见许多条件声明,它们是用来处理每个分支中相似的操作。这提供给我们一个想法,提取一个返回具体类型的对象并且根据具体对象行为执行操作的工厂类。

    例如在下面,让我们定义一个带有单独的apply方法的操作接口

    public interface Operation {
    int apply(int a, int b);
    }
    这方法带有两个数值类型参数以及数值类型的返回。让我们来定义一个实现加法的类:

    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 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);
    }
    在这个例子里,我们能看到如何通过工厂类来将逻辑业务责任分发委托给一系列轻耦合对象当中。但是如果只是简单地将嵌套if语句转移成工厂类,这明显是不符合我们的目的的。

    作为另外的选择,我们能够通过维护能够被快速查询的对象仓库map(映射),正如OperatorFactory#operationMap,来达成我们的目的。我们也能够在运行时定义映射对象并且配置它们用于查找。

    3.2 使用枚举

    除了映射对象(map)的使用之外,我们也可以使用枚举来标记特定的逻辑业务。在这之后,我们能通过它来代替嵌套if语句或者swtich语句了。作为其他处理,我们也可以使用它们作为对象工厂并且整理用于处理相关的业务逻辑操作。

    这会减少嵌套if语句的数量并且将业务责任委托给独立的枚举变量中。

    让我们来看看怎么去实现它。首先,我们需要定义一个枚举类:

    public enum Operator {
    ADD, MULTIPLY, SUBTRACT, DIVIDE
    }
    像我们看到这样,这些值是不同操作符的标签,并且会运用到之后的计算当中。就像嵌套if语句和switch语句那样,我们可以将这些值当作选项来使用。但和它们不同的地方,让我们去设计一种能够将逻辑委托给枚举本身的替代方法吧。

    我们为每一个枚举量都定义了各自的方法并且进行了计算操作,例如:

    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);
    }
    现在,我们可以通过使用Operator#valueOf()方法来将字符串转换为操作符来调用方法了:

    @Test
    public void whenCalculateUsingEnumOperator_thenReturnCorrectResult() {
    Calculator calculator = new Calculator();
    int result = calculator.calculate(3, 4, Operator.valueOf("ADD"));
    assertEquals(7, result);
    }
    3.3 命令模式(command pattern)

    在先前的讨论中,我们已经看到使用工厂类来返回指定操作符的对应的业务对象实例了,稍后,业务对象实例将用于之后的Claculator中执行计算操作。

    我们也能够设计一个Calculator#calculate方法来接收一个可以执行输入的指令。这是另外一种来代替嵌套if语句的方法。

    首先我们定义一个Command接口:

    public interface Command {
    Integer execute();
    }
    然后,让我们实现其中的一个AddCommand:

    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();
    }
    通过实例化AddCommand对象并且将他作为参数传递到Calculator#calculate方法当中用以调用计算方法:

    @Test
    public void whenCalculateUsingCommand_thenReturnCorrectResult() {
    Calculator calculator = new Calculator();
    int result = calculator.calculate(new AddCommand(3, 7));
    assertEquals(10, result);
    }
    3.4 规则引擎(rule engine)

    当我们最终编写了大量的嵌套if语句时,每一个条件都描述了特定的业务规则,用于评估正确逻辑操作的执行。规则引擎将这些复杂的草从主代码中去掉。规则引擎是用于评估规则并且基于输入返回结果。

    让我们通过设计一个简单的规则引擎来做下试验。这个引擎是通过一组规则来处理表达式,并从选中的规则返回结果。首先,我们定义一个规则接口:

    public interface Rule {
    boolean evaluate(Expression expression);
    Result getResult();
    }
    接着,我们来实现一个规则引擎:

    public class RuleEngine {
    private static List 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();
    }
    }
    这个规则引擎接收一个表达式并且返回Result。现在,我们设计一个带有两个数值变量以及一个用于操作的Operator对象的表达式(Expression)类:

    public class Expression {
    private Integer x;
    private Integer y;
    private Operator operator;
    }
    最后,我们定义一个AddRule类,它只在加操作中使用到:

    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;
    }
    }
    现在,我们能够使用Expression来调用RuleEngine了:

    @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());
    }
    4. 总结

    在这次教程中,我们探索了一系列不同的方法来简化复杂的代码。同时我们也学到了这么去使用有效的设计模式来取代繁杂的嵌套fi声明语句。

  • 相关阅读:
    [Linux]
    [.Net]
    [.Net]
    [Linux]
    [Google]
    面向对象的7个基本设计原则
    windows SDK中的wininet写http客户端
    C++ 用libcurl库进行http通讯网络编程
    感悟
    关于Windows高DPI的一些简单总结
  • 原文地址:https://www.cnblogs.com/bendantuohai/p/5803803.html
Copyright © 2011-2022 走看看