在类似if-else增长时应该使用什么样的设计模式?

时间:2015-10-22 10:44:42

标签: java design-patterns

我们有一些代码:

public class ErrorCodeUtil {
    public static void handleErrorCode(String errorCode) {
        if (errorCode.equals("1")) {
            handleErrorCode1();
        } else if (errorCode.equals("2")) {
            handleErrorCode2();
        } else if (errorCode.equals("3")) {
            handleErrorCode3();
        } else {
            handleErrorCodeByDefault(errorCode);
        }
    }

    public static void logByErrorCode(String errorCode) {
        if (errorCode.equals("1")) {
            logErrorCode1();
        } else if (errorCode.equals("2")) {
            logErrorCode2();
        } else if (errorCode.equals("3")) {
            logErrorCode3();
        } else {
            logErrorCodeByDefault(errorCode);
        }
    }
    //... a lot of method about error code
}

如您所见,我们有Util来处理ErrorCode的所有内容,当我们想要为错误代码添加特殊逻辑时,我们必须更改该utils类的许多方法。

正如预期的那样,错误代码的值在很大范围内变化(可能是" 112345"或" error_code_001")。那么什么设计模式适用于那种情况呢?

4 个答案:

答案 0 :(得分:3)

我会实现decision table

该表将包含一个或多个Predicate作为键和Function作为值之间的一组映射。如果满足Predicate条件,则执行相应的Function。如果未满足Predicate条件,则应执行默认Function。这可以(轻松)取代巨大的" if-else"陈述,应该更容易维护。

Predicate应该如何?它应该是String(在您的情况下)并且应该返回boolean,指示条件是否满足:

interface Predicate {
    public boolean test(String x);
}

在决策表中,您将此接口的(匿名)实现添加为键。

提示:如果你已经使用Java8,那就更好了,那就是内置的Predicate<T>接口。但如果您不是,那么您可以引入自己的Predicate界面。 : - )

决策表值的Function将是一个类似的界面。它可能(或可能不)使用输入参数并应返回void。在Java8中,这称为Consumer,但在我的示例中,我将坚持Function命名:

interface Function<T> {
    void apply(T t);
}

通过在Predicate作为键和Function<ErrorCodeUtil>作为值之间构建对,我们将填充决策表。当满足Predicate条件时,我们会调用相应的Function .apply()方法:

决策表本身可以是一个简单的Map<Predicate, Function<ErrorCodeUtil>>

Map<Predicate, Function<ErrorCodeUtil>> decisionTable = new HashMap<>();

你应该在施工时或任何你想要的地方填充它(就在handleErrorCode()方法逻辑之前):

Predicate equalsOne = new Predicate() { 
    public void test(String x) {
         return "1".equals(x);
    }
};
Function<ErrorCodeUtil> actionOne = new Function<ErrorCodeUtil>() {
    public void apply(ErrorCodeUtil t) {
        t.handleErrorCode1();
    }
}

decisionTable.put(equalsOne, actionOne);

等对于其他&#34;条件 - 动作&#34;对,包括else将始终返回Predicate的默认操作(即最后true语句)。

请注意,在Java8中,只需使用lambdas就可以显着减少这些匿名类。

最后,你的&#34; if-elseif&#34;语句将被重新考虑到一个简单的循环:

for (Map.Entry<Predicate, Function<ErrorCodeUtil>> entry: decisionTable.entrySet()){
    Predicate condition = entry.getKey();
    Function<ErrorCodeUtil> action = entry.getValue();
    if (condition.test(errorCode)) {
        action.apply(this);
    }
}

因此,每次添加新条件时,您都不必触及handleErrorCode(String error)方法,但是您必须引入{{1}的新(匿名)实现}和Predicate以及Function将其放入决策表。

答案 1 :(得分:2)

在这种情况下我会使用Enum。

public enum ErrorCodeEnum {
    1 {

        @Override
        public void handleErrorCode() {
            //doSomething
        }
    },
    2 {

        @Override
        public void handleErrorCode() {
            //doSomething
        }
    };

  public abstract void handleErrorCode();

}

然后,手中有错误代码......

ErrorCodeEnum.valueOf("1").handleErrorCode();

PS:正如你所问,这就是我用来替换if-else语句的方法。但我会针对该特定问题使用Logger API(似乎您正在记录错误)。

答案 2 :(得分:1)

您可以将列表中的所有错误代码保留在一个类中。并检查列表是否包含错误代码。 因此,这会降低您的if...else逻辑。

您已编写了不同的方法来处理错误代码,例如handleErrorCode1()handleErrorCode2()等。现在,如果列表包含所需的错误代码,那么您可以通过java反射调用这些方法。

答案 3 :(得分:0)

关于错误的记录,如果所需要的只是将代码与消息匹配,则具有代码到消息的映射的文本文件是正确的方式。文本文件可能是属性:

1=Item not Found
2=Item not valid

可以加载到java.util.Properties实例,它可能是可以加载到DOM或HashMap中的xml

<errors>
  <error>
    <code>1</code>
    <msg>Item not Found</msg>
  </error>
  <error>
    <code>2</code>
   <msg>Item not Valid</msg>
  </error>
<errors>

这种方法的一个优点是,如果您在文件名中指定语言代码,然后从客户端获取用户语言代码,则可以使其支持i18n