mercredi 25 janvier 2017

Design pattern for surrounding code in start, complete and fail methods

Suppose I have various arbitrary sections of code to run, but before each section, I have to run a Start() method and then after each section I need to run a Complete() method. However, if an exception is thrown in the code section, I want to run a Fail(string message) method instead of Complete(). Is there a design pattern that elegantly encapsulates this to make it neat and easily repeatable?

For example, let's say I have a type called Thing that contains a Start() method that adds a row to a logging db table to reflect that a task is in progress, a Complete() method that changes that row to reflect that the task finished and a Fail(string message) method that changes the row to reflect that the task failed. These are just examples though, they could be doing any set-up and tidy up type tasks.

The naive implementation might be simply to call those methods manually:

public void DoStuff()
{
    var thing = new Thing();
    thing.Start();
    try
    {
        DoImportantStuff();
        thing.Complete();
    }
    catch (Exception e)
    {
        thing.Fail(e.Message);
    }
}

But if I'm going to have to repeat this in a lot of different places, it ends up creating quite a lot of duplication and it might be easy to forget to call Complete or mess this up in some subtle way.

In C#, there's the using pattern, which provides a good way of encapsulating most of this. For example, if my Thing type looked like this:

public class Thing : IDisposable
{
    public Thing(){
        Start();
    }

    private void Start() { /* start */ }
    private void Complete() { /* complete */ }

    public void Dispose()
    {
        Complete();
    }
}

My DoStuff() method could now be simplified to this:

public void DoStuff()
{
    using(new Thing())
    {
        DoImportantStuff();
    }
}

Which is much nicer. But it doesn't allow me to call Fail instead of Complete if an exception is thrown because (I think!) the Dispose method is essentially called in a Finally block.

I have thought of having a try/catch inside the using block and then setting a thing.HasFailed flag inside the catch block and then using that in the Dispose method to decide whether to Complete or Fail. But that seems a bit fiddly and I'd like the consumer of Thing to have to do as little as possible to make it work correctly.

So is there a design pattern that encapsulates what I want to do and avoids the need to manually write a try\catch each time?

Aucun commentaire:

Enregistrer un commentaire