【问题标题】:Design Pattern for Error Handling错误处理的设计模式
【发布时间】:2014-02-23 22:47:39
【问题描述】:

我在各种项目中多次遇到这个问题,我想知道是否有比我通常最终使用的解决方案更好的解决方案。

假设我们有一系列需要执行的方法,并且我们想知道其中一个方法是否出现问题并优雅地中断(可能撤消任何以前的更改......),我通常会这样做以下(伪 C#,因为这是我最熟悉的):

private bool SomeMethod()
{
    bool success = true;
    string errorMessage = null;
    success = TestPartA(ref errorMessage);
    if (success)
    {
        success = TestPartB(ref errorMessage);
    }
    if (success)
    {
        success = TestPartC(ref errorMessage);
    }
    if (success)
    {
        success = TestPartD(ref errorMessage);
    }
        //... some further tests: display the error message somehow, then:
        return success;
}

private bool TestPartA(ref string errorMessage)
{
    // Do some testing...
    if (somethingBadHappens)
    {
       errorMessage = "The error that happens";
       return false;
    }
    return true;
}

我只是想知道(这是我的问题)是否有更好的方法来应对这种事情。我似乎最终写了很多 if 声明,因为它看起来应该更流畅。

有人建议我对一组委托函数进行循环,但我担心这会过度设计解决方案,除非有一种干净的方法来做到这一点。

【问题讨论】:

  • 如果这些确实是错误,那么您可能应该抛出异常。

标签: c# asp.net design-patterns error-handling custom-error-handling


【解决方案1】:

您或许可以尝试查看"Open/Closed" section of the SOLID Principle. 在您的示例中,您或许可以创建一个ITestRule 接口,其中包含一个名为CheckRule() 的方法,该方法将更新您的消息并返回bool。然后,您将为要测试的每个规则创建一个接口实现,并将该类添加到 List<ITestRule> 对象。从上面的 Redmondo 示例中,我将更改为以下内容:

var discountRules =
                new List<ITestRule>
                    {
                        new TestPartA(),
                        new TestPartB(),
                        new TestPartC(),
                        new TestPartD(),
                    };

然后,您会将新的List&lt;ITestRule&gt; 传递给评估器,该评估器将遍历每个类并运行CheckRule() 方法。

【讨论】:

  • 这会起作用(我将来可能需要这样的东西),但我认为我会为我需要的东西过度设计而感到内疚。
【解决方案2】:

我认为您可能应该使用异常。请注意,您通常应该只在应用程序的“顶级”捕获异常。

private void TopLevelMethod()
{
    try
    {
        SomeMethod();
    }
    catch (Exception ex)
    {
        // Log/report exception/display to user etc.
    }
}

private void SomeMethod()
{
    TestPartA();
    TestPartB();
    TestPartC();
    TestPartD();
}

private void TestPartA()
{
    // Do some testing...
    try
    {
        if (somethingBadHappens)
        {
            throw new Exception("The error that happens");
        }
    }
    catch (Exception)
    {
        // Cleanup here. If no cleanup is possible, 
        // do not catch the exception here, i.e., 
        // try...catch would not be necessary in this method.

        // Re-throw the original exception.
        throw;
    }
}

private void TestPartB()
{
    // No need for try...catch because we can't do any cleanup for this method.
    if (somethingBadHappens)
    {
        throw new Exception("The error that happens");
    }
}

我在示例中使用了内置的 System.Exception 类;您可以创建自己的派生异常类,或使用派生自 System.Exception 的内置异常类。

【讨论】:

  • 我认为你是对的;感谢壳牌冲击。我从没想过抛出自己的异常/重新抛出异常,这几乎涵盖了所有场景。
【解决方案3】:

我试图坚持一个被称为“快速失败”的原则;方法应该在应该失败时失败,并立即返回错误的详细信息。然后,调用方法会做出适当的响应(将异常重新抛出给调用者,记录详细信息,如果它是 UI 绑定方法则显示错误等):-

http://en.wikipedia.org/wiki/Fail-fast

但是,这并不意味着使用异常来控制应用程序的流程。当你可以处理它时仅仅提出一个异常通常是不好的做法:-

http://msdn.microsoft.com/en-us/library/dd264997.aspx

在您的情况下,我会将您的代码重写为(例如):-

private bool SomeMethod()
{
    bool success = false;

    try
    {
        TestPartA();
        TestPartB();
        TestPartC();
        TestPartD();

        success = true;
    }
    catch (Exception ex)
    {
        LogError(ex.Message);
    }

    //... some further tests: display the error message somehow, then:
    return success;
}

private void TestPartA()
{
    // Do some testing...
    if (somethingBadHappens)
    {
        throw new ApplicationException("The error that happens");
    }
}

【讨论】:

  • 与 ShellShock 的答案几乎相同,只是慢了一点!不过,我喜欢快速失败的原则;宝贵的阅读材料。
  • 来自 msdn:"您应该从 Exception 类而不是 ApplicationException 类派生自定义异常。您不应在代码中抛出 ApplicationException 异常,并且不应捕获 ApplicationException 异常,除非你打算重新抛出原来的异常。"
猜你喜欢
  • 1970-01-01
  • 2011-03-12
  • 2011-02-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-10-25
  • 2016-03-15
  • 1970-01-01
相关资源
最近更新 更多