代码之家  ›  专栏  ›  技术社区  ›  Cesar

在检查条件时,消除代码中嵌套ifs的最佳方法是什么?

  •  3
  • Cesar  · 技术社区  · 15 年前

    我正在用Java开发一个BlackBerry应用程序,我有一个选项类,其中存储了所有用户设置。问题是我需要检查一些情况,以便知道如何反应。随着我不断添加更多的功能,更多的GUI选项显示给用户,更多的设置存储在options类中,需要检查更多的条件。

    private void doCallMonitoring(int callId){
        /*This is the part that I want to avoid. Having
          multiple nested ifs. Here's just two conditions
          but as I add more, it will get unmantainable
          very quickly.*/
        if(Options.isActive().booleanValue()){
            callTime = new Timer();
            TimerTask callTimeTask = new TimerTask(){
                public void run(){
                    callTimeSeconds++;
            if((callTimeSeconds == Options.getSoftLimit().intValue()) && (Phone.getActiveCall().getStatus() == PhoneCall.STATUS_CONNECTED)){
                        injectDTMFTone(Phone.getActiveCall());
            }else if((callTimeSeconds >= Options.getHardLimit().intValue()) && (Phone.getActiveCall().getStatus() == PhoneCall.STATUS_CONNECTED)){
                        injectEndCall();
                    }
                 }
            };     
            callTime.schedule(callTimeTask, 0,1000);
        }else{
        System.out.println("Service not active");
        }
    }
    

    我希望它如何工作,就是通过一次电话来验证所有选项,并从中确定行动的诅咒。如何实现这样的设计?

    4 回复  |  直到 15 年前
        1
  •  4
  •   Jared Oberhaus    15 年前

    另一种选择是制作方法,例如 injectDMTFTone() 检查他们是否希望处理该条件,并根据是否处理该条件返回true或false。

    例如:

    public void run() {
        callTimeSeconds++;
        do {
            if (handleInjectDMTFTone())
                break;
            if (handleInjectEndCall())
                break;
        } while(false);
    
        callTime.schedule(callTimeTask, 0,1000);
    }
    
    boolean handleInjectDMTFTone() {
        if ((callTimeSeconds != Options.getSoftLimit().intValue()) ||
            (Phone.getActiveCall().getStatus() != PhoneCall.STATUS_CONNECTED))
            return false;
    
        injectDTMFTone(Phone.getActiveCall());
        return true;
    }
    
    boolean handleInjectEndCall() {
    
        if ((callTimeSeconds < Options.getHardLimit().intValue()) ||
            (Phone.getActiveCall().getStatus() != PhoneCall.STATUS_CONNECTED))
            return false;
    
        injectEndCall();
        return true;
    }
    

    当然,不是给别人打电话 注入dmtftone() 方法或 injectEndCall() 方法,您只需将该逻辑直接内联到这些方法中。通过这种方式,您将如何以及何时在同一位置处理这些情况的所有逻辑组合在一起。

    这是我最喜欢的模式之一;使用 if

    您可以通过创建所有实现相同接口的对象,并在您的 run 方法可以迭代以查看哪个将处理它。这对你的案子来说可能有点过分了,也可能没有。

        2
  •  2
  •   Community kavare    7 年前

    您可以使用“extract method”重构,并将所有这些检查转换为一个“可读”条件。

    看这个相关的 answer 有点长,但关键是要替换如下构造:

           }else if((callTimeSeconds >= Options.getHardLimit().intValue()) && (Phone.getActiveCall().getStatus() == PhoneCall.STATUS_CONNECTED)){
                    injectEndCall();
                }
             }
    

    对于这样的事情:

           ....
           }else if(shouldInjectEndCall() ){
                    injectEndCall();
                }
             }
           ...
    

    请记住,对象确实具有状态,并且可以使用其他对象来帮助它们完成其工作。

        3
  •  1
  •   OscarRyz    15 年前

    尽管看起来只是编写更多的代码,但您可以用“validator”对象替换所有这些规则,并将所有验证都放在某个数组中并循环执行。

    类似于这个临时代码。

      private void doCallMonitoring(int callId){
         // Iterate the valiators and take action if needed. 
    
          for( Validation validation : validationRules ) { 
              if( validation.succeed() ) { 
                  validation.takeAction();
              }
          }
       }
    

    您可以这样实现它们:

    abstract class Validation { 
    
          public boolean suceed();
          public void takeAction();
    }
    
    class InjectDTMFToneValidation extends Validation { 
        public boolean suceed() { 
            return (callTimeSeconds == Options.getSoftLimit().intValue()) 
                   && (Phone.getActiveCall().getStatus() == PhoneCall.STATUS_CONNECTED)
         }
         public void takeAction() { 
             injectDTMFTone(Phone.getActiveCall());
         }
    }
    
    class InjectEndCallValidation extends Validation { 
        public boolean suceed() { 
            return (callTimeSeconds >= Options.getHardLimit().intValue()) 
                    && (Phone.getActiveCall().getStatus() == PhoneCall.STATUS_CONNECTED)
         }
         public void takeAction() { 
             injectEndCall();
         }
    }
    

    最后在列表中安装它们:

    private List<Validation> validationRules = new ArrayList<Validation>();{
       validationrules.add( new InjectDTMFToneValidation() );
       validationrules.add( new InjectEndCallValidation () );
       ...
       ...
    }
    

    这里的想法是将逻辑转移到子类。当然,你会得到一个更好的结构,也许 成功的 采取行动 可以替换为其他更有意义的方法,目的是从现有的位置提取验证。

    它变得更抽象了。。对

        4
  •  1
  •   Bill K    15 年前

    所有这些答案可能都是更好的OO答案。就这一次,我要找一个又快又脏的答案。

    if(!Options.isActive().booleanValue()) {
        System.out.println("Service not active");
        return;
    }
    the rest...
    

    我知道有些人不喜欢mid方法返回,但当您验证对我来说一直是一个很棒的模式的输入条件时,我从不后悔使用它。

    它确实简化了方法的外观。

    如果您编写的方法比屏幕长,请不要这样做,也不要写一条大注释指出它——很容易丢失return语句而忘记您所做的。更好的是,不要编写比屏幕更长的方法。