用模式替换if else语句

时间:2015-01-20 15:26:23

标签: java oop design-patterns

我有一个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条件。有什么建议吗?

5 个答案:

答案 0 :(得分:34)

这是Refactoring to Patterns一书中的经典Replace Condition dispatcher with Command

enter image description here

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

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);
}

答案 1 :(得分:15)

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

在设计模式方面,这往往是战略模式 Replace Conditional Logic with Strategy

如果你有一个小的,有限的条件,我建议使用 enum 来实现策略模式(在枚举中提供一个抽象方法并覆盖它对于每个常数)。

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 SomeCondition(String parameter){
       this.parameter = parameter;
   }

   public String getParameter(){
       return parameter;
   }

}


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

}

答案 2 :(得分:1)

让我们假设我们有这样的代码(与您的代码相同):

    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();
    }

最好以其他条件包含在工厂中的方式进行设计,并且开发人员有意调用它。在这种情况下,当条件不满足时,我们抛出异常。或者,我们可以完全按照问题来编写它。如果您愿意,可以使用.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()方法将基本策略接口更改为抽象类,并对该抽象类进行新的实现。其他代码保持不变。

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";
    }
}

枚举+枚举有点儿“工厂”
我们可以使用Enum来实现策略,而可以使用枚举中的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

命令+工厂
strategy和command之间的区别是命令也保持状态,因此,例如,如果您拥有compute(int a,int b,String someCondition),并且想要通过包括其签名更改在内的strategy对其进行重构,则可以将其简化为compute(int a,int b,ComputeStrategycomputeStrategy),可以使用命令将其简化为一个参数compute(ComputeCommandcomputeCommand)。在这种情况下,我们也可以像策略模式一样利用多态性。

    CommandConditionFactory commandConditionFactory = new CommandConditionFactory();

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

最好以其他条件包含在工厂中的方式进行设计,并且开发人员有意调用它。在这种情况下,当条件不满足时,我们抛出异常。或者,我们可以完全按照问题来编写它。如果您愿意,可以使用.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),并且仅更改参数。
抽象类:

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));
    }
}

是否嵌套
请注意,即使您嵌套了ifs,有时也可以对其进行重构并使用上述技术之一。 可以说我们有以下代码:

    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();
        }
    }

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

规则引擎
如前所述,当您嵌套了无法重构的ifs时,您可以实现自己的简单规则引擎。仅当您有许多嵌套的if时才应使用它,否则它是形式胜于内容的形式。
对于非常复杂的业务逻辑,有像Drools这样的专业规则引擎。
我不会在那儿提供实现。

还有一件事
在您提供的示例中,很有可能有人介绍了这些if,但是它们完全是多余的。我们可以通过尝试重构决定方法签名以使其采用其他一些参数并重构正在调用我们方法的周围代码来对其进行检查。通过这样做,我们摆脱了工厂方法。有一些示例展示了这些if冗余时代码的外观。

策略
决定方法:

    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之类的简单类型,因为它来自外部系统,或者条件基于输入中的整数,因此我们无法重构代码很容易。

答案 3 :(得分:0)

我想你一定已经考虑过了,但如果你使用的是JDK 7或更高版本,你可以打开字符串。这样你的代码看起来比一堆if-else语句更清晰。

答案 4 :(得分:0)

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

public interface Operation {

String process(String a, String b);
}

该方法以两个字符串作为输入并返回结果。

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);
  }
}

然后我们应该定义一个工厂类,该类基于给定的运算符返回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);
      }
    }