简体   繁体   English

错误处理的设计模式

[英]Design Pattern for Error Handling

I've ran into this problem a few times on various projects, and I've wondered if there's a better solution than the one I normally end up using. 我在各个项目中遇到过这个问题几次,我想知道是否有比我通常最终使用的解决方案更好的解决方案。

Say we have a series of methods that need to execute, and we want to know if something goes wrong within one of the methods and break out gracefully (potentially undo-ing any previous changes...), I typically do the following (pseudo C# because it's what I'm most familiar with): 假设我们有一系列需要执行的方法,并且我们想知道其中一个方法中是否出现问题并优雅地突破(可能撤消任何先前的更改......),我通常会执行以下操作(伪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;
}

I just wondered (and this is my question) if there's a better methodology for coping with this kind of thing. 我只是想知道(这是我的问题),如果有更好的方法来应对这种事情。 I seem to end up writing a lot of if statements for something that seems like it should be slicker. 我似乎最终写了很多if语句似乎应该更流畅。

I've been suggested having a loop over a set of delegate functions, but I'd be worried that would be over-engineering the solution, unless there's a clean way to do it. 我被建议在一组委托函数上进行循环,但是我担心这会过度设计解决方案,除非有一个干净的方法来完成它。

I think you should probably be using exceptions. 我认为你应该使用例外。 Note you should generally only be catching exceptions at the "top level" in your application. 请注意,通常只应在应用程序的“顶层”捕获异常。

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");
    }
}

I have used the built-in System.Exception class in my example; 我在我的例子中使用了内置的System.Exception类; you can create your own derived exception classes, or use the built-in ones derived from System.Exception. 您可以创建自己的派生异常类,也可以使用从System.Exception派生的内置类。

You could perhaps try looking at the "Open/Closed" section of the SOLID Principle. 您也许可以尝试查看SOLID原则“打开/关闭”部分。 In your example you could perhaps create an ITestRule interface which contains a method called CheckRule() that will updated your message and return a bool . 在您的示例中,您可以创建一个ITestRule接口,其中包含一个名为CheckRule()的方法,该方法将更新您的消息并返回一个bool You would then create an interface implementation for each rule you want to test, and add that class to a List<ITestRule> object. 然后,您将为要测试的每个规则创建一个接口实现,并将该类添加到List<ITestRule>对象。 From the Redmondo example above, I would change to the following: 从上面的Redmondo示例中,我将更改为以下内容:

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

You would then pass the new List<ITestRule> to an evaluator which will loop through each of the classes and runs the CheckRule() method. 然后,您将新的List<ITestRule>传递给一个求值器,它将循环遍历每个类并运行CheckRule()方法。

I try to stick to a principle known as 'Fail Fast'; 我试图坚持一个称为“快速失败”的原则; methods should fail when they are supposed to, and return immediately with details of the error. 方法应该在它们应该发生时失败,并立即返回错误的详细信息。 The calling method then responds appropriately (re-throw the exception to its caller, log the details, show an error if it's a UI-bound method, etc): - 然后调用方法适当地响应(将异常重新抛出给它的调用者,记录细节,如果它是一个UI绑定方法则显示错误等): -

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

However, this does not mean using exceptions to control the flow of your application. 但是,这并不意味着使用异常来控制应用程序的流程。 Just raising an exception when you could deal with it is generally bad practice: - 刚养的时候,你可以对付它通常是不好的做法异常: -

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

In your case, I'd re-write your code as (for example): - 在你的情况下,我会重新编写你的代码(例如): -

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");
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM