【问题标题】:Improving legibility on conditional statement提高条件语句的易读性
【发布时间】:2013-09-12 08:19:49
【问题描述】:

我正在为我的安卓设备构建一个 HTTP 服务器。

我使用了很多 IF-ELSE 语句来处理不同的请求。

由于我将与其他人共享我的代码以供以后使用,因此我必须使其尽可能清晰易读。现在,我什至无法轻松阅读我的代码。

我认为问题出在一个类中使用了很多 IF-ELSE 语句。 例如。

if(purpose.equals("readProfile"){
     .....
}
else if(purpose.equals("writeProfile"){
     .....
}
    ....

我尝试将它们按类别分类,并根据它们的类别对条件进行排序。但并没有改善很多易读性。 然后我尝试在每个条件之前编写简短的 cmets。但这让事情变得更加混乱。

可以做些什么来提高条件语句的易读性?

【问题讨论】:

  • 为一组请求定义类似的行为,并创建特定的接口来保存这些请求的通用方法和类实现来保存这些请求的特定业务逻辑。您可以创建一个工厂来接收例如用途("readProfile""writeProfile")并返回接口,这样您就不必为这些 if-else 语句而烦恼。
  • 您可以访问antiifcampaign.com等网站了解更多信息
  • 除了@LuiggiMendoza所说的,可以在Enums中定义目的。它提高了可读性,可以在任何地方使用,而不是在所有地方都硬编码字符串!

标签: java android readability code-readability


【解决方案1】:

正如Luiggi Mendoza 所说,这是a previous question 的后续行动...

如果您使用的是 Java 7,则可以使用switch-case statement for strings

    //month is a String
    switch (month.toLowerCase()) {
        case "january":
            monthNumber = 1;
            break;
          //partsleft out for sake of brevity ..
        default: 
            monthNumber = 0;
            break;
    }

(摘自上面引用的 Oracle Java 教程。)

重构

但是,这个巨大的 if-else 只是问题的一部分。由于这似乎是随着时间的推移而增长的结构,我建议进行彻底的重构,并使用在我看来是Strategy pattern。你应该:

制定一个涵盖所有用例边界的接口:

interface MyStrategy {
  void execute(MyInputContext input, MyOutputContext output);
}

(在 MyInputContext 和 MyOutputContext 中使用 void 方法只是一种方法,这只是一个示例,但要处理有响应的请求,这很有意义,就像 Servlet 的工作方式一样)

将大 IF-ELSE 语句的内容重构为该接口的实例(这些将是策略):

//VERY simplified...
class ReadProfileStrategy implements MyStrategy {
  void execute(MyInputContext input, MyOutputContext output) {
    //do the stuff that was in the if-else block in the "readProfile" part
  }
}

//... at the branching part:
MyInputContext input; //build this here
MyOutputContext output; //build this here

switch (purpose) {
    case "readProfile":
         // no need to always instantiate this, it should be stateless...
         new ReadProfileStrategy().execute();
         break;
    //... left out for sake of brevity
}

重构步骤 2

如果这样做了,您可以将字符串 ID 添加到接口和实例本身,并完全摆脱 if-else 或 switch 语句,您甚至可以创建一个通过 IOC 容器填充的 Map(如) ,保持最新,并且完全灵活。

class ReadProfileStrategy implements MyStrategy {
  String getID() {
      return "readProfile";
  }

  void execute(MyInputContext input, MyOutputContext output) {
    //do the stuff that was in the if-else block in the "readProfile" part
  }
}

在处理请求时的类中

private final Map<String, MyStrategy> strategyMap; //fill the map using your favorite approach, like using Spring application context, using the getCode() to provide the key of the map

在处理逻辑中:

MyStrategy strategy = strategyMap.get(purpose);
if(strategy!=null) {
    strategy.execute();
}
else {
    //handle error here
}

【讨论】:

  • 很高兴指出这个问题是 OP 最新问题的后续问题:stackoverflow.com/q/18757692/1065197
  • @LuiggiMendoza 我昨天什至看到了这个问题,但我已经忘记了......
【解决方案2】:

这可能超出范围,但只是一个观察

尝试使用

if("readProfile".equals(purpose){} 而不是

if(purpose.equals("readProfile"){}.

这将有助于避免 null pinter 异常

【讨论】:

  • 大部分时间我都这样做,但在某些情况下,此解决方案会干扰“快速失败”概念 - 在某些用例中未检测到空值可能会很糟糕。
【解决方案3】:

枚举可以提供帮助 - 您还可以向它们添加功能。

public void test(String purpose) {
  if (purpose.equals("readProfile")) {
    // Read.
  } else if (purpose.equals("writeProfile")) {
    // Write.
  }
}

enum Purpose {
  readProfile {
    @Override
    void doIt() {
      // Read.
    }
  },
  writeProfile {
    @Override
    void doIt() {
      // Write.
    }
  };

  abstract void doIt();

}
public void test2(String purpose) {
  Purpose.valueOf(purpose).doIt();
}

【讨论】:

  • +1 我也想过这个,但我担心这似乎是一个请求-响应处理的东西,在很多情况下,我可能会三思而后行:代码仍然是在一个地方,太过分了。如果每个部分都足够小(干净的代码,比如说方法主体中最多 5 行),它会有所帮助。但除此之外,它只会让事情变得丑陋 - 但这只是一种观点,因为我只是在过去几天里碰巧这样做了,它有点失控了。
【解决方案4】:

您可以尝试为每个块使用某种带有实现的动作接口,并使用该动作的具体实现预加载地图。

interface Action {
    void execute();
}

Map<String, Action> actions = new HashMap<>();
actions.put("readProfile", new Action() { ... });
actions.put("writeProfile", new Action() { ... });

actionMap.get(purpose).execute();    

这也会降低你的圈复杂度。当然,您应该只预加载一次地图。

【讨论】:

    【解决方案5】:

    好吧,如果将 if-else 条件中的代码分离到另一个类是有意义的,也许可以使用工厂模式。还要让所有分离的类都实现通用接口(例如:MyActivity.class),方法是execute()

    工厂根据您传递的字符串决定必须创建什么对象(ReadProfile.classWriteProfile.class 等),然后调用execute() 方法。

    MyActivity obj = MyFactory.createMyActivity(String)
    obj.execute(...);
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-07-14
      • 1970-01-01
      • 2017-02-10
      • 1970-01-01
      • 1970-01-01
      • 2021-04-24
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多