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