假设我有各种任意的代码段要运行,但在每一段之前,我必须运行一个Start()
方法,然后在每一节之后,我需要运行一个Complete()
方法。但是,如果在代码部分中抛出异常,我希望运行Fail(string message)
方法,而不是Complete()
。是否有一种设计模式优雅地封装了这一点,使其整洁且易于重复?
例如,假设我有一个名为Thing
的类型,它包含一个Start()
方法,该方法将一行添加到日志数据库表中以反映任务正在进行中,一个Complete()
方法,它将更改该行以反映任务已完成,一个Fail(string message)
方法,它更改该行来反映任务失败。不过,这些只是示例,它们可以执行任何设置和整理类型的任务。
天真的实现可能只是手动调用这些方法:
public void DoStuff()
{
var thing = new Thing();
thing.Start();
try
{
DoImportantStuff();
thing.Complete();
}
catch (Exception e)
{
thing.Fail(e.Message);
}
}
但是,如果我不得不在很多不同的地方重复这一点,它最终会产生大量的重复,可能很容易忘记调用Complete
,或者以某种微妙的方式将其搞砸。
在C#中,有using
模式,它提供了一种封装大部分内容的好方法。例如,如果我的Thing
类型看起来像这样:
public class Thing : IDisposable
{
public Thing(){
Start();
}
private void Start() { /* start */ }
private void Complete() { /* complete */ }
public void Dispose()
{
Complete();
}
}
我的DoStuff()
方法现在可以简化为:
public void DoStuff()
{
using(new Thing())
{
DoImportantStuff();
}
}
这要好得多。但是,如果抛出异常,它不允许我调用Fail
而不是Complete
,因为(我认为!)Dispose
方法本质上是在Finally
块中调用的。
我已经考虑过在using
块内具有try/catch
,然后在catch
块内设置thing.HasFailed
标志,然后将其用于Dispose
方法来决定是Complete
还是Fail
。但这似乎有点麻烦,我希望Thing
的消费者必须尽可能少地做才能使其正常工作。
那么,是否有一种设计模式可以封装我想要做的事情,并避免每次手动编写trycatch
?
您可以拥有这样的Thing
:
public class Thing
{
private void Start() { /* start */ }
private void Complete() { /* complete */ }
private void Fail(string message) {}
public void DoAction(Action action)
{
this.Start();
try
{
action();
this.Complete();
}
catch (Exception e)
{
this.Fail(e.Message);
}
}
}
像这样使用:
Thing thing = new Thing();
thing.DoAction(this.DoStuff);
该模式被称为"模板方法"。您可以在标题"面向方面编程"下找到您的实现。(https://msdn.microsoft.com/en-us/library/aa288717(v=vs.71).aspx)
使用委托。
public class Thing : IDisposable
{
private void Start() { /* start */ }
private void Complete() { /* complete */ }
private void Fail(string _szMessage) {/* fail */}
public delegate void ProcessClientStuff();
private ProcessClientStuff m_delegateClientStuff;
public Thing(ProcessClientStuff _delegateClientStuff) {m_delegateClientStuff = _delegateClientStuff}
public void Dostuff()
{
Start();
try
{
m_delegateClientStuff();
Complete();
}
catch(Exception e)
{
Fail(e.Message);
}
}
}
void ClientStuff()
{
Console.WriteLine("Hello");
}
Thing oClientStuffProcessor = new Thing(ClientStuff);
oClientStuffProcessor.Dostuff();