用模式替换 if else 语句

Replacing if else statement with pattern

我有一个 if else 语句,在不久的将来可能会增加。

    public void decide(String someCondition){

        if(someCondition.equals("conditionOne")){
            //
            someMethod("someParameter");

        }else if(someCondition.equals("conditionTwo")){

           //
           someMethod("anotherParameter");

        }
        .
        .
        else{

            someMethod("elseParameter");

        }
}

因为,这看起来已经很乱了,我想如果我能在这里应用任何设计模式会更好。我研究了策略模式,但我不确定这是否会减少这里的 if else 条件。有什么建议吗?

这是《重构模式》一书中的经典Replace Condition dispatcher with Command

基本上,您为旧 if/else 组中的每个代码块创建一个 Command 对象,然后为这些命令创建一个映射,其中键是您的条件字符串

interface Handler{
    void handle( myObject o);
}


 Map<String, Handler> commandMap = new HashMap<>();
 //feel free to factor these out to their own class or
 //if using Java 8 use the new Lambda syntax
 commandMap.put("conditionOne", new Handler(){
         void handle(MyObject o){
                //get desired parameters from MyObject and do stuff
          }
 });
 ...

那么您的 if/else 代码是:

 commandMap.get(someCondition).handle(this);

现在如果以后需要添加新命令,只需添加到散列即可。

如果要处理默认情况,可以使用 Null Object 模式来处理地图中不存在条件的情况。

 Handler defaultHandler = ...

if(commandMap.containsKey(someCondition)){
    commandMap.get(someCondition).handle(this);
}else{
    defaultHandler.handle(this);
}

我想你一定已经考虑过了,但如果你使用的是JDK7或以上,你可以打开字符串。这样你的代码看起来比一堆 if-else 语句更干净。

Martin Fowler 的一般建议是 Replace Conditional with Polymorphism.

就设计模式而言,这通常是策略模式 Replace Conditional Logic with Strategy.

如果您有一个小而有限的条件集,我建议使用枚举来实现策略模式(提供一个枚举中的抽象方法并为每个常量覆盖它)。

public enum SomeCondition{
   CONDITION_ONE{

       public void someMethod(MyClass myClass){
              //...
       }
   },

   CONDITION_TWO{

       public void someMethod(MyClass myClass){
       }

   }

   public abstract void someMethod(MyClass myClass);

}

public class MyClass{
//...
    public void decide(SomeCondition someCondition){
        someCondition.someMethod(this);
    }

}

如果您真的只是想选择一个参数,那么您可以这样定义枚举:

public enum SomeCondition{
   CONDITION_ONE("parameterOne"),

   CONDITION_TWO("parameterTwo");

   private final String parameter;

   private SomeCondition(String parameter){
       this.parameter = parameter;
   }

   public String getParameter(){
       return parameter;
   }

}


public class MyClass{
//...
    public void decide(SomeCondition someCondition){
        someMethod(someCondition.getParameter());
    }

}

解决当前问题的另一种方法是使用工厂模式。这提供了提取 returns 给定类型对象并根据具体对象行为执行操作的工厂方法的功能。

public interface Operation {

String process(String a, String b);
}

该方法将两个字符串作为输入,returns 结果。

public class Concatenation implements Operation {

  @Override
  public String process(String a, String b) {
    return a.concat(b);
  }
}

public class Join implements Operation {

  @Override
  public String process(String a, String b) {
    return String.join(", ", a, b);
  }
}

然后我们应该定义一个工厂 class,其中 returns 个基于给定运算符的 Operation 实例:

public class OperatorFactory {

  static Map<String, Operation> operationMap = new HashMap<>();

  static {
    operationMap.put("concatenation", new Concatenation());
    operationMap.put("join", new Join());
    // more operators
  }

  public static Optional<Operation> getOperation(String operator) {
    return Optional.ofNullable(operationMap.get(operator));
  }
}

现在我们可以使用它了:

public class SomeServiceClass {

      public String processUsingFactory(String a, String b, String operationName) {
        Operation operation = OperatorFactory
            .getOperation(operationName)
            .orElseThrow(() -> new IllegalArgumentException("Invalid Operation"));

        return operation.process(a, b);
      }
    }

假设我们有这样的代码(和你的一样):

    public void decide(String someCondition) {
        if(someCondition.equals("conditionOne")) {
            someMethod("someParameter");
        }
        else if(someCondition.equals("conditionTwo")) {
            someMethod("anotherParameter");
        }
        else {
            someMethod("elseParameter");
        }
    }

假设您不想重构应用程序的其他部分并且不想更改方法签名,则可以通过以下可能的方式重构它:

警告 - 您应该使用上述模式的通用版本。
我展示了非通用的,因为它们更容易阅读。

策略+工厂方法
我们可以使用策略和工厂方法模式。我们还利用了多态性。

  private final StrategyConditionFactory strategyConditionFactory = new StrategyConditionFactory();

    public void decide(String someCondition) {
        Strategy strategy = strategyConditionFactory.getStrategy(someCondition)
                .orElseThrow(() -> new IllegalArgumentException("Wrong condition"));
        strategy.apply();
    }

最好设计成else condition 包含在工厂中,开发者有意调用。在这种情况下,当条件不满足时我们抛出异常。或者,我们可以完全按照所讨论的那样编写它。如果你想要这样而不是 .orElseThrow(() -> new IllegalArgumentException("Wrong condition"));.orElse(new ElseStrategy());

StrategyConditionFactory(工厂方法):

    public class StrategyConditionFactory {
        private Map<String, Strategy> conditions = new HashMap<>();
    
        public StrategyConditionFactory() {
            conditions.put("conditionOne", new ConditionOneStrategy());
            conditions.put("conditionTwo", new ConditionTwoStrategy());
            //It is better to call else condition on purpose than to have it in the conditional method
            conditions.put("conditionElse", new ElseStrategy());
            //...
        }
    
        public Optional<Strategy> getStrategy(String condition) {
            return Optional.ofNullable(conditions.get(condition));
        }
    }

策略界面:

public interface Strategy {
    void apply();
}

实现:

    public class ConditionOneStrategy implements Strategy {
        @Override
        public void apply() {
            //someMethod("someParameter");
        }
    }  
    public class ConditionTwoStrategy implements Strategy {
        @Override
        public void apply() {
            //someMethod("anotherParameter")
        }
    }
    public class ElseStrategy implements Strategy {
        @Override
        public void apply() {
            //someMethod("elseParameter")
        }
    }

用法(简体):

    public void strategyFactoryApp() {
        //...
        decide("conditionOne");
        decide("conditionTwo");
        decide("conditionElse");
        //...
    }

策略 + 工厂方法 - 这种特殊情况(只有参数发生变化)
我们可以利用这样一个事实,在这种情况下我们总是调用相同的方法,只是参数发生变化
我们使用 getParameter() 方法将我们的基本策略接口更改为抽象 class,并且我们对该抽象 class 进行了新的实现。其他代码保持不变。

public abstract class Strategy {
    public abstract String getParameter();

    public void apply() {
        someMethod(getParameter());
    }

    private void someMethod(String parameter) {
        //someAction
    }
}

实现:

public class CondtionOneStrategy extends Strategy {
    @Override
    public String getParameter() {
        return "someParameter";
    }
}
public class CondtionTwoStrategy extends Strategy {
    @Override
    public String getParameter() {
        return "anotherParameter";
    }
}
public class ElseStrategy extends Strategy {
    @Override
    public String getParameter() {
        return "elseParameter";
    }
}

枚举 + 枚举有点像“工厂”
我们可以使用枚举来实现策略,而不是工厂方法,我们可以使用枚举中的 valueOf()。

    public void decide(String someCondition) {
            ConditionEnum conditionEnum = ConditionEnum.valueOf(someCondition);
            conditionEnum.apply();
        }

条件枚举:

public enum ConditionEnum {
    CONDITION_ONE {
        @Override
        public void apply() {
            //someMethod("someParameter");
        }
    },
    CONDITION_TWO {
        @Override
        public void apply() {
            //someMethod("anotherParameter");
        }
    },
    CONDITION_ELSE {
        @Override
        public void apply() {
            //someMethod("elseParameter");
        }
    };
    //...more conditions

    public abstract void apply();
}

用法(简体):

    public void enumFactoryApp() {
        //...
        decide("CONDITION_ONE");
        decide("CONDITION_TWO");
        decide("CONDITION_ELSE");
        //...
    }

请注意,当枚举类型没有具有指定名称的常量时,您将得到 IllegalArgumentException

命令 + 工厂
策略和命令之间的区别在于命令也保持状态,所以如果你有例如 compute(int a, int b, String someCondition) 并且你想用策略重构它,包括它的签名更改,你可以将它减少到 compute(int a, int b, ComputeStrategy computeStrategy) 使用命令可以将其减少为一个参数 compute(ComputeCommand computeCommand)。在这种情况下,我们也利用类似于策略模式情况的多态性。

    CommandConditionFactory commandConditionFactory = new CommandConditionFactory();

    public void decide(String someCondition) {
        Command command = commandConditionFactory.getCommand(someCondition)
                .orElseThrow(() -> new IllegalArgumentException("Wrong condition"));
        command.apply();
    }

最好设计成else condition 包含在工厂中,开发者有意调用。在这种情况下,当条件不满足时我们抛出异常。或者,我们可以完全按照所讨论的那样编写它。如果你想要这样而不是 .orElseThrow(() -> new IllegalArgumentException("Wrong condition"));.orElse(new ElseCommand());

CommandConditionFactory(工厂方法):

public class CommandConditionFactory {
    private Map<String, Command> conditions = new HashMap<>();

    public CommandConditionFactory() {
        conditions.put("conditionOne", new ConditionOneCommand("someParameter"));
        conditions.put("conditionTwo", new ConditionTwoCommand("anotherParameter"));
        //It is better to call else condition on purpose than to have it in the conditional method
        conditions.put("conditionElse", new ElseCommand("elseParameter"));
        //...
    }

    public Optional<Command> getCommand(String condition) {
        return Optional.ofNullable(conditions.get(condition));
    }
}

命令接口:

public interface Command {
    void apply();
}

实现(有一些冗余,但它是为了展示命令在更一般的情况下应该如何看待,而不是 someMethod() 我们有三种不同的方法):

public class ConditionOneCommand implements Command {
    private final String parameter;

    public ConditionOneCommand(String parameter) {
        this.parameter = parameter;
    }

    @Override
    public void apply() {
        //someMethod(parameter);
    }
}
public class ConditionTwoCommand implements Command {
    private final String parameter;

    public ConditionTwoCommand(String parameter) {
        this.parameter = parameter;
    }

    @Override
    public void apply() {
        //someMethod(parameter);
    }
}
public class ElseCommand implements Command {
    private final String parameter;

    public ElseCommand(String parameter) {
        this.parameter = parameter;
    }

    @Override
    public void apply() {
        //someMethod(parameter);
    }
}

用法(简体):

    public void commandFactoryApp() {
        //...
        decide("conditionOne");
        decide("conditionTwo");
        decide("conditionElse");
        //...
    }

命令 + 工厂 - 这种特殊情况。
这实际上不是一个真正的命令模式,只是一个派生物。它利用了这样一个事实,即在这种情况下,我们总是调用相同的方法 someMethod(parameter) 并且只有参数发生变化。
摘要class:

public abstract class Command {
    abstract void apply();

    protected void someMethod(String parameter) {
        //someAction
    }
}

实施(所有 3 个条件情况都相同):

public class CommandImpl extends Command {
    private final String parameter;

    public CommandImpl (String parameter) {
        this.parameter = parameter;
    }

    @Override
    public void apply(){
        someMethod(parameter);
    }
}

工厂请注意,只有一个命令实现,只有参数变化:

public class CommandConditionFactory {
    Map<String, Command> conditions = new HashMap<>();

    public CommandConditionFactory() {
        conditions.put("conditionOne", new CommandImpl("someParameter"));
        conditions.put("conditionTwo", new CommandImpl("anotherParameter"));
        //It is better to call else condition on purpose than to have it in the conditional method
        conditions.put("conditionElse", new CommandImpl("elseParameter"));
        //...
    }

    public Optional<Command> getCommand(String condition) {
        return Optional.ofNullable(conditions.get(condition));
    }
}

嵌套 if 的
请注意,即使您有嵌套的 if,有时也可以重构它们并使用上述技术之一。 假设我们有以下代码:

    public void decide2(String someCondition, String nestedCondition) {
        if(someCondition.equals("conditionOne")) {
            if(nestedCondition.equals("nestedConditionOne")){
                someLogic1();
            }
            else if(nestedCondition.equals("nestedConditionTwo")){
                someLogic2();
            }
        }
        else if(someCondition.equals("conditionTwo")) {
            if(nestedCondition.equals("nestedConditionThree")){
                someLogic3();
            }
            else if(nestedCondition.equals("nestedConditionFour")){
                someLogic4();
            }
        }
    }

您可以使用数学逻辑规则重构它:

    public void decide2(String someCondition, String nestedCondition) {
        if(someCondition.equals("conditionOne")
                && nestedCondition.equals("nestedConditionOne")) {
            someLogic1();
        }
        else if(someCondition.equals("conditionOne")
                && nestedCondition.equals("nestedConditionTwo")) {
            someLogic2();
        }
        else if(someCondition.equals("conditionTwo")
                && nestedCondition.equals("nestedConditionThree")) {
            someLogic3();
        }
        else if(someCondition.equals("conditionTwo")
                && nestedCondition.equals("nestedConditionFour")) {
            someLogic4();
        }
    }

然后你可以使用策略、枚举或命令。您只有一对字符串 而不是单个字符串。

决策表
当您有嵌套的 ifs 无法如前所述重构时,您可以实施自己的决策表或使用一些现成的决策表解决方案。我不会在那里给出实现。

规则引擎
当您嵌套的 ifs 无法如前所述重构时,您还可以实现自己的简单规则引擎。只有当你有很多嵌套的 if 时才应该使用它,否则它就是形式战胜内容。
对于非常复杂的业务逻辑,有专业的规则引擎,如 Drools。
我不会在那里给出实现。

还有一件事
在您给出的示例中,很可能有人引入了这些 if,但它们完全是多余的。我们可以通过尝试重构决定方法签名以使其采用其他参数并重构调用我们方法的周围代码来检查它。通过这样做,我们摆脱了我们的因素方法。有一些示例展示了当这些 ifs 是多余的时代码的外观。

策略
判断方法:

    public void decide(Strategy strategy) {
        strategy.apply();
    }

用法(简体):

    public void strategyApp() {
        //...
        decide(new ConditionOneStrategy());
        decide(new ConditionTwoStrategy());
        decide(new ElseStrategy());
        //...
    }

枚举
判断方法:

    public void decide(ConditionEnum conditionEnum) {
        conditionEnum.apply();
    }

用法(简体):

    public void enumApp() {
        //...
        decide(ConditionEnum.CONDITION_ONE);
        decide(ConditionEnum.CONDITION_TWO);
        decide(ConditionEnum.CONDITION_ELSE);
        //...
    }

命令
判断方法:

    public void decide(Command command) {
        command.apply();
    }

用法(简体):

    public void commandApp() {
        //...
        decide(new ConditionOneCommand("someParameter"));
        decide(new ConditionTwoCommand("anotherParameter"));
        decide(new ElseCommand("elseParameter"));
        //...
    }

事实上这是非常特殊的情况,在某些情况下我们必须使用像 String 这样的简单类型,因为它来自外部系统或者条件是基于输入的整数所以我们不能重构代码如此简单。