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

时间:2009-03-06 23:03:43

标签: java design-patterns blackberry

我正在用Java开发一个BlackBerry应用程序,我有一个Options类,可以存储所有用户设置。问题是我需要检查一些条件才能知道如何反应。随着我不断添加更多功能,向用户显示更多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 个答案:

答案 0 :(得分:4)

另一种选择是使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;
}

当然,您可以在这些方法中直接内联逻辑,而不是调用另一个injectDMTFTone()方法或injectEndCall()方法。通过这种方式,您将所有关于如何以及何时处理这些条件的逻辑分组在同一个地方。

这是我最喜欢的模式之一;使用if语句尽可能接近方法的顶部,以消除条件和返回。该方法的其余部分没有缩进多个级别,并且易于阅读。

您可以通过创建所有实现相同接口并位于处理程序存储库中的对象来进一步扩展它,您的run方法可以迭代以查看哪个将处理它。对你的案子来说,这可能有点过分,也可能不过分。

答案 1 :(得分:2)

您可以使用“提取方法”重构,并将所有这些检查置于一个“可读”状态。

看到这个相关的answer有点冗长,但重点是替换这样的结构:

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

对于这样的事情:

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

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

答案 2 :(得分:1)

其他选择是做一些“用多态性替换条件”。

虽然看起来只是编写更多代码,但您可以用“验证器”对象替换所有这些规则,并在一些数组中进行所有验证并循环遍历它们。

像这个划痕代码。

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

这里的想法是将逻辑移动到子类。当然,您将获得更好的结构,并且可能 suceed takeAction 可以替换为其他更有意义的方法,目标是从其所在位置进行验证。

变得更抽象? ..是的。

顺便说一句,为什么你要使用Options和Phone类来调用静态方法而不是使用实例?

答案 3 :(得分:1)

所有这些答案可能都是更好的OO答案。有一次,我会选择快速而肮脏的答案。

我真的想通过反转来简化复杂的嵌套ifs。

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

我知道有些人不喜欢中途返回,但是当你确认一直对我来说一直很棒的入场条件时,我从来没有后悔使用它。

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

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