【问题标题】:How to avoid a lot of if else conditions如何避免很多 if else 条件
【发布时间】:2012-04-27 21:10:13
【问题描述】:

我已经阅读了很多关于代码重构和避免 if else 语句的主题。实际上,我有一门课使用了很多 if - else 条件。

更多细节:我正在使用拉解析器,在我的肥皂响应的每一行,我会检查是否有我感兴趣的标签,如果没有,检查另一个标签等:

 if(eventType == XmlPullParser.START_TAG) {
            soapResponse= xpp.getName().toString();
            
            if (soapResponse.equals("EditorialOffice")){  
                eventType = xpp.next();
                if (xpp.getText()!=null){
                editorialOffice += xpp.getText();
                }
            }   
            else if (soapResponse.equals("EditorialBoard")){  
                eventType = xpp.next();
                if (xpp.getText()!=null){
                editorialBoard += xpp.getText();
                }
            }
            else if (soapResponse.equals("AdvisoryBoard")){  
                eventType = xpp.next();
                if (xpp.getText()!=null){
                advisoryBoard += xpp.getText();
                }
            }   
        }
        eventType = xpp.next();
     }

现在,我想使用别的东西,而不是那些 if else 条件,但我不知道是什么。

你能举个例子吗?

【问题讨论】:

  • 您可以在程序的其他地方维护字符串到枚举的映射,从映射中提取与返回的字符串关联的枚举(如果字符串不在map) 并在枚举上写一个 switch 语句。它可能会使这段代码更清晰,但代价是额外的间接层。你必须判断这是否值得。
  • 检查这个:stackoverflow.com/questions/519422/…希望这有帮助
  • 旁注,出于性能原因,您应该使用 StringBuilder 而不是 += 将字符串连接在一起。
  • 谢谢 Sam Barnum,如何使用字符串生成器?我会尝试在互联网上检查,但一个例子会很棒。
  • StringBuilder sb = new StringBuilder(); sb.append(editorialBoard); sb.append(xpp.getText); editorialBoard=sb.toString();

标签: java if-statement refactoring


【解决方案1】:

在这种特殊情况下,由于除了附加的字符串之外,所有 3 种情况的代码基本相同,因此我将为每个正在构建的字符串创建一个映射条目:

Map<String,String> map = new HashMap<String,String>();
map.put("EditorialOffice","");
map.put("EditorialBoard","");
map.put("AdvisoryBoard","");
// could make constants for above Strings, or even an enum

然后将您的代码更改为以下内容

if(eventType == XmlPullParser.START_TAG) {
    soapResponse= xpp.getName().toString();
    String current = map.get(soapResponse);
    if (current != null && xpp.getText()!=null) {
        map.put( soapResponse, current += xpp.getText());
    }
    eventType = xpp.next();
}

没有“如果……那么……否则”。甚至不会增加策略模式等多个类的复杂性。地图是您的朋友。策略在某些情况下很棒,但这个很简单,不用也可以解决。

【讨论】:

    【解决方案2】:

    你可以像下面这样定义一个枚举:

    public enum SoapResponseType {
        EditorialOffice(1, "description here") {
            public void handle(XmlPullParser xpp) {
                //do something you want here
                return null;
            }
        },
        EditorialBoard(2, "description here") {
            public void handle(XmlPullParser xpp) {
                //do something you want here
                return null;
            }
        },
        AdvisoryBoard(3, "description here") {
            public void handle(XmlPullParser xpp) {
                //do something you want here
                return null;
            }
        };
    
        public static SoapResponseType nameOf(String name) {
            for (SoapResponseType type : values()) {
                if (type.getName().equalsIgnoreCase(name)) {
                    return type;
                }
            }
            return null;
        }
    
        public void handle(XmlPullParser xpp) {
            return null;
        }
    }
    

    像这样使用上面的枚举:

    SoapResponseType type = SoapResponseType.nameOf("input string");
    if (type != null) {
        type.handle(xpp);
    }
    

    这是干净的代码,不是吗!

    【讨论】:

      【解决方案3】:

      尝试查看策略模式。

      • 创建一个接口类来处理响应 (IMyResponse)
        • 使用此 IMyResponse 创建 AdvisoryBoardResponse、EditorialBoardResponse 类
      • 创建一个字典,将 soapresponse 值作为键,将您的策略​​作为值
      • 然后您可以通过从字典中获取 IMyResponse 类的方法来使用它

      小例子:

      // Interface
      public interface IResponseHandler {
         public void handleResponse(XmlPullParser xxp);
      
      }
      
      // Concrete class for EditorialOffice response
      private class EditorialOfficeHandler implements IResponseHandler {
         public void handleResponse(XmlPullParser xxp) {
             // Do something to handle Editorial Office response
         }
      }
      
      // Concrete class for EditorialBoard response
      private class EditorialBoardHandler implements IResponseHandler {
         public void handleResponse(XmlPullParser xxp) {
             // Do something to handle Editorial Board response
         }
      }
      

      在您需要创建处理程序的地方:

      Map<String, IResponseHandler> strategyHandlers = new HashMap<String,IResponseHandler>();
      strategyHandlers.put("EditorialOffice", new EditorialOfficeHandler());
      strategyHandlers.put("EditorialBoard", new EditorialBoardHandler());
      

      您收到回复的地点:

      IResponseHandler responseHandler = strategyHandlers.get(soapResponse);
      responseHandler.handleResponse(xxp);
      

      【讨论】:

      • 谢谢@hwcverwe,在我看来这是个好主意。你能否给我一个更精确的例子来说明如何做到这一点?非常感谢。
      • 也许这适用于更复杂的多条件决策。但这对于这种特殊情况来说是不是太沉重了?哎呀,在这种特殊情况下,我什至更喜欢“if, then. else if”而不是这个解决方案(尽管存在其他更简单的解决方案)。
      • 我不认为战略太复杂。在这种情况下,似乎期望的结果是附加到通过选择器选择的正在进行的字符串。有时策略是明确的选择,即当操作根据选择器而改变时。如果作者在 15 个 if-else 条件下的情况相似,我会讨厌为相同的操作实现 15 个类,而唯一不同的是正在操作的目标。如果操作可能因选择器而有所不同,那么策略有助于适应预期的变化,这将成为一个很好的选择。
      • +1:我已经按照您对处理 XML 应用程序的建议做了很多工作。
      • 这看起来很方便。快速提问:我认为这会比“大量if 语句的性能更好,因为我们将直接使用O(1). 获取密钥?还有你有任何显示此类性能基准的博客/文章吗?
      【解决方案4】:

      您没有提到是否可以或确实使用 Java 7。从该 Java 版本开始,您可以使用 Strings in switch statements

      除此之外,封装每个案例的逻辑是个好主意,例如:

      Map<String, Department> strategyMap = new HashMap<String, Department>();
      strategyMap.put("EditorialOffice", new EditorialOfficeDepartment());
      strategyMap.put("EditorialBoard", new EditorialBoardDepartment());
      strategyMap.put("AdvisoryBoard", new AdvisoryBoardDepartment());
      

      然后您可以简单地从地图中选择正确的策略并使用它:

      String soapResponse = xpp.getName();
      Department department = strategyMap.get(soapResponse);
      department.addText(xpp.getText());
      

      Department当然是在界面中...

      【讨论】:

      • 在工作上的所有干扰之后,我终于提交了答案,但@hwcverwe 同时已经提交了一个几乎相同的答案(见上文)。
      【解决方案5】:

      您可以创建一个具有三个实现的 ResponseHandler 接口,一个用于 if/else 构造的每个分支。

      然后有一个映射不同的soapResponses到一个处理程序,或者一个包含所有处理程序的列表,如果它可以处理那个soapResponse。

      您还应该能够将一些样板代码移动到响应处理程序类的常见可能抽象实现中。

      通常有很多变化。通过利用代码重复,实际上只需要一种实现:

      class ResponseHandler{
          String stringToBuild = "" // or what ever you need
          private final String matchString
      
          ResponseHandler(String aMatchString){
              matchString = aMatchString
          }
          void handle(XppsType xpp){
              if (xpp.getName().toString().equals(matchString){
                  eventType = xpp.next();
                  if (xpp.getText()!=null){
                       editorialOffice += xpp.getText();
                  }
              }
          }
      }
      

      你的代码变成了

      List<ResponseHandler> handlers = Arrays.asList(
          new ResponseHandler("EditorialOffice"),
          new ResponseHandler("EditorialBoard"),
          new ResponseHandler("AdvisoryBoard"));
      if(eventType == XmlPullParser.START_TAG) {
          for(ResponseHandler h : handlers)
              h.handle(xpp);
      }
      

      【讨论】:

      • 谢谢@Jean Sxhauder,你能给我一个如何使用它的例子吗?
      • 添加了一个示例实现
      【解决方案6】:

      这是一个巨大的问题,没有真正的答案。 (而且我不经常使用肥皂)

      这里只是基于您的代码的一些想法:

      首先你可以对重复的代码进行分组

      if (soapResponse.equals("EditorialOffice")
      ||soapResponse.equals("EditorialBoard")
      ||soapResponse.equals("AdvisoryBoard")){ 
      

      您可以做的另一件好事是玩转开关,例如:

      switch(soapResponse){
      case "EditorialOffice":
      case "EditorialBoard":
      case "AdvisoryBoard":
      eventType = xpp.next();
                      if (xpp.getText()!=null){
                      advisoryBoard += xpp.getText();
                      }
      break;
      

      您还应该考虑将您的测试分解为小功能:

      public bool interestingTag(string s){
      return (soapResponse.equals("EditorialOffice")
          ||soapResponse.equals("EditorialBoard")
          ||soapResponse.equals("AdvisoryBoard"));
      }
      
          public processData(xpp){
          eventType = xpp.next();
                          if (xpp.getText()!=null){
                          editorialBoard += xpp.getText();
                          }
          ....}
      

      这样你就可以在一个while循环中处理你所有的答案,如果else变成一个5~10行的函数,你就会超长

      但正如我所说,有很多好方法可以做同样的事情

      【讨论】:

        【解决方案7】:

        在 Java 7 中,您可以在字符串上进行 SWITCH。如果你可以使用它,你可以使用它;-)

        【讨论】:

        • +1 表示“如果你可以使用它,你可以使用它。”如果我们有鸡蛋,我们可以有火腿和鸡蛋,如果我们有火腿。
        • 如果你想让代码更干净,开关不会让代码更干净
        【解决方案8】:

        除了 zzzzzzz(etc.) 的评论...请记住,您使用的是 XmlPullParser,它会让您编写像您拥有的那样丑陋的代码。您可以注册一些回调来拆分您的代码并使其“更好”,但如果可能,只需使用 SimpleXML 库或类似库。

        此外,您可以重构代码以使其更具可读性和更少冗长。例如,为什么在每个 if 语句中调用 xpp.next()?为什么不只在外面调用一次:

        if(eventType == XmlPullParser.START_TAG) {
            soapResponse= xpp.getName().toString();
            if (soapResponse.equals("EditorialOffice") && xpp.getText()!=null){  
                editorialOffice += xpp.getText();
            }   
            else if (soapResponse.equals("EditorialBoard") && xpp.getText()!=null){  
                editorialBoard += xpp.getText();
            }
            else if (soapResponse.equals("AdvisoryBoard") && xpp.getText()!=null){  
                advisoryBoard += xpp.getText();
            }   
        }
        eventType = xpp.next();
        

        【讨论】:

        • 谢谢@Cristian,事实上,我正在从服务器获取 xml 响应,我不知道是否可以使用其他东西,就像 XML 拉解析器一样。我在每个语句上调用 xpp.next() ,而当找到我要查找的开始标记时,在下一行我会找到我需要的变量,并将其放入我的局部变量中。
        • 同样XmlPullParser.getName()返回String,所以不需要调用.toString()就可以了。
        • @Ana 看看 SimpleXML 库;它会让你的生活更轻松、更快乐。
        • @Cristian,再次感谢,请问您有如何使用它的示例吗?
        • 文档非常完整。设置完所有内容后,它就像执行以下操作一样简单:SomeObject oneObject = Xml.parse(SomeObject.class, xmlPayload);
        猜你喜欢
        • 2019-03-27
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-01-28
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多