【问题标题】:Refactor big switch case combined with if clauses结合 if 子句重构 big switch case
【发布时间】:2020-02-20 22:15:31
【问题描述】:

所以我有这样的事情:

switch (data){
    case "one" :
        if (version1) 
            createDataOneV1();
         else 
             createDataOnetV2();
         break;
    case "two":
        if(version1)
            createDataTwoV1();
        else 
            createDataTwoV2();
        break;
    default:
        createDefaultData();
}

有很多(开关)案例。 有什么重构的建议吗?

【问题讨论】:

  • 您可以添加一个采用 (String data, boolean versionFlag)createData 方法 - 然后它将根据两个参数进行分支,并调用 createDatamethods。仍然很丑,但你的整个 switch 消失了。

标签: java java-8 switch-statement refactoring


【解决方案1】:

不要使用Map。它不会为您提供与 switch 相同的编译时安全性。

如果你仍然想摆脱它,那么我建议使用enum

public enum DataCreationStrategy {

    ONE("one", DataCreator::createDataOneV1, DataCreator::createDataOneV2),
    TWO("two", DataCreator::createDataTwoV1, DataCreator::createDataTwoV2)
    // ... other cases
    ;

    private final String key;
    private final Function<DataCreator, String> creator;
    private final Function<DataCreator, String> defaultCreator;

    DataCreationStrategy(String key, Function<DataCreator, String> creator, Function<DataCreator, String> defaultCreator) {
        this.key = key;
        this.creator = creator;
        this.defaultCreator = defaultCreator;
    }

    public static Function<DataCreator, String> of(String key, boolean flag) {
        for (DataCreationStrategy strategy: values()){
            if(strategy.key.equals(key)){
                return flag ? strategy.creator : strategy.defaultCreator;
            }
        }
        return DataCreator::createDefaultData;
    }

}

然后像这样使用它:

String createdData = DataCreationStrategy.of(key, versionFlag).apply(creator);

(您可以将String替换为您需要生成的实际数据类型)


of 方法也可以以 Stream API 方式实现。但在这种特殊情况下,普通的旧 for 循环要干净得多。

public static Function<DataCreator, String> of(String key, boolean flag) {
   return Arrays.stream(values())
                .filter(s -> s.key.equals(key))
                .findAny()
                .map(s -> flag ? flag ? s.creator : s.defaultCreator )
                .orElse(DataCreator::createDefaultData);
}

【讨论】:

  • 但是如果只有部分数据有 V3 怎么办?
【解决方案2】:

首先确定您是否真的需要重构该代码。 丑吗?当然。但它简单明了。您可以使用额外的抽象层对其进行清理,但这通常会导致代码变得不那么明显,并且通常会降低性能。

无论如何,如果您想重构该代码,您可以使用类型系统。定义一个DataCreator接口,可以为V1V2生成数据。您可以使用如下函数选择正确的DataCreator

DataCreator getDataCreatorForData(String data){
    switch(data) {
       case "one": return new DataCreatorOne();
       case "two": return new DataCreatorTwo();
       default: return new DefaultDataCreator()
    }
}

一旦你有了DataCreator,你检查版本:

DataCreator creator = getDataCreatorForData(data);
if (version1){
    creator.createV1Data();
}else{
    creator.createV2Data();
}

这种结构比问题中的结构更扁平,嵌套更少。

【讨论】:

    【解决方案3】:

    您可以为HashMap 创建一个Key 和一些Supplier(例如):

    @RequiredArgsConstructor
    class SpecialKey  {
         private final String data;
         private final boolean second;
         // hashCode/equals
    }
    

    假设您的createDataOneV1createDataOneV2 返回boolean

    Map<SpecialKey, Supplier<Boolean>> map = new HashMap<>();
    map.put(new SpecialKey("one", true), this::createDataOneV1);
    map.put(new SpecialKey("one", false), this::createDataOneV2);
    

    然后简单地说:

    String data = ... // get the "data" somehow
    boolean version = ...
    
    boolean res = map.getOrDefault(new SpecialKey(data, version), this::createDefaultData).get();
    

    【讨论】:

      【解决方案4】:

      使用字符串作为键和函数作为值创建映射。

      【讨论】:

      • 可能会比Map&lt;String, Map&lt;Boolean, Consumer/Supplier/Function&gt;&gt; 更好,具体取决于createData*** 方法。
      • @Naman 或 Map&lt;Pair&lt;String,Boolean&gt;, Consumer/Supplier/Function&gt;Map&lt;DedicatedObjectDocumentingSemantic, Consumer/Supplier/Function&gt;...
      猜你喜欢
      • 2023-04-08
      • 1970-01-01
      • 2015-11-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-09-10
      相关资源
      最近更新 更多