在例外中提供附加信息的可持续方法?

时间:2013-01-06 00:08:43

标签: java

我正在寻找一种方法来为最终用户提供异常的多条信息。扩展Exception的显而易见的解决方案最终是文本分布式吞吐量代码,例如

throw new MyException("Bad data", "The data you entered is incorrect", "http://www.example.com/moreinfo/baddata");

这很快就变得不可行了。

然后我查看了一个目录方法,但是它过于集中,每次抛出异常时都需要从一个文件跳转到另一个文件。我现在正在考虑使用静态ErrorInfoMap类的混合方法,该类包含从键到更详细信息的映射。然后每个类都有一个静态部分,其中包含自己的错误映射,因此使用抛出上述异常的类作为示例我将其更改为:

throw new MyException("Bad data");

并且在课程的底部会有类似的内容:

static {
  ErrorInfoMap.put("Bad data", new ErrorInfo("The data you entered is incorrect", "http://www.example.com/moreinfo/baddata"));
  // Information for other exceptions thrown by this class goes here
}

允许异常处理程序在需要时获取其他信息。这是解决这个问题的好方法,还是有更好的方法来解决这个问题?

3 个答案:

答案 0 :(得分:1)

您的混合方法的替代方法是将错误映射放在异常本身中。在MyException初始化Bad data后添加您已展示的ErrorInfo,同时为MyException提供一系列允许您覆盖或补充的构造函数Bad data的含义的默认定义。

答案 1 :(得分:1)

您可以始终将“MyException”作为超类,并将特定类型的错误作为其子类型。在错误消息方面,您可以在子类型上使用静态常量来存储不同类型的错误。

E.g

Exception    
    -> MyException
      -> BadDataException
      -> InvalidUserException

会像这样抛出:

throw new BadDataException(BadDataException.DATA_TOO_LONG);

答案 2 :(得分:1)

我不确定“目录方法”究竟是什么意思(你能提供参考或更详细的描述吗?)但是根据你提供的信息,我不清楚静态ErrorInfoMap如何避免问题每次抛出异常时“过于集中并且[需要]从一个文件跳到另一个文件”。

对我而言,有几种选择,具体取决于您需要完成的任务:

  1. 创建一个扩展ExceptionTemplate的根类Exception,并执行您希望所有例外操作的可重复行为。格式化toString()方法就是一个很好的例子。根据您的确切目标,您可能希望让异常实现构建器模式,如下所示:

    throw new BadDataException("Supplied data is not valid")
          .expected("a positive integer")
          .referenceUrl("http://www.example.com/moreinfo/baddata");
    
  2. 避免使用枚举或子类所在的stringly-typed解决方案。如果您不需要在运行时定义新的异常类型(如果您这样做,那应该是一个红色标记,表明您的设计存在更严重的错误)并且具有包含构建异常所需的所有必要信息的枚举: / p>

    public class EnumException extends Exception {
        private EnumException() {} // prevent objects from being instantiated elsewhere
    
        public enum Type {
            BAD_DATA("Bad Data","Supplied data is not valid", "http://www.example.com/moreinfo/baddata"),
            MISSING_DATA("Missing Data","Required data not found", "http://www.example.com/moreinfo/missingdata");
    
            Type(String title, String genericMessage, String url) {
                // Store input
            }
    
            public EnumException exception() {
                // construct your exception
                return new EnumException();
            }
        }
    }
    

    可以通过以下方式调用:

    // note no `new` - the exception is returned by the enum
    throw EnumException.Type.BAD_DATA.exception().expected("a positive integer");
    

    这样做的好处是可以确保编译时类型的安全性,同时还可以灵活地在一个地方定义不同类型的异常。

  3. 创建大量例外。我仍然不完全确定你有什么异议只是创造了一堆例外。您正在寻找“提供额外信息”的方法,但声称“扩展Exception的明显解决方案最终会以文本分布式吞吐量代码”。情况并非如此。除了只能在施工时提供的信息外,Exception的每个子类都应包含所有必要的信息。因此,应该有最小的“文本分布在整个代码中”,因为任何样板/可重用字符串都应该在Exception类中,而不是其他地方。

    public class DocumentedException extends Exception
    {
        private String title;
        private String genericMessage;
        private String detailedMessage;
        private String url;
    
        // Generally avoid this long constructor, and define subclasses that only need detailedMessage
        public DocumentedException(String t, String g, String d, String u) {
            super(g + " - " + d); // sets the getMessage() return value to something sane
            title = t;
            genericMessage = g;
            detailedMessage = d;
            url = u;
        }
    
        public String toString() {
            return title.toUpperCase()+"\n"+
                genericMessage+"\n"+
                detailedMessage+"\n"+
                "More info on this error: "+url+"\n";
        }
    
        public static class BadData extends DocumentedException {
            public BadData(String details) {
                super("Bad Data", "Supplied data is not valid", details, "http://www.example.com/moreinfo/baddata");
            }
        }
    
        public static class MissingData extends DocumentedException {
            public MissingData(String details) {
                super("Missing Data", "Required data not found", details, "http://www.example.com/moreinfo/missingdata");
            }
        }
    }
    

    然后你可以简单地用:

    打电话
    throw new DocumentedException.BadData("Username cannot contain whitespace");
    

    当然,如果您预计需要定期警告用户名错误,您可以创建一个额外的类:

    public static class BadUsername extends BadData {
        public BadUsername() {
            super("Usernames can only contain letters, numbers, and underscores");
        }
    }
    

    同样,目标是明确定义异常层次结构,以处理您预期需要处理的所有情况,以避免在整个应用程序中重复定义相同的字符串。我个人喜欢上面使用的group-exceptions-into-inner-classes模式,它可以让你非常清楚你的错误而不需要创建数百个你需要不断查看的愚蠢的存根java文件。我想说每个主要包都应该有一个关联的异常保持类,它定义了该包的所有必要的异常。