这里并没有真正确定的答案,但以下是我使用过几次并且效果很好的解决方案。 (不仅用于异常处理,还用于所有横切关注点)。
一种可能的方法是使用装饰器模式。我写了一篇关于这个的帖子,你可以在这里查看:http://www.kenneth-truyers.net/2014/06/02/simplify-code-by-using-composition/
我还建议您查看 Greg Young 关于大致相同主题的视频:http://www.infoq.com/presentations/8-lines-code-refactoring
为了使用装饰器模式,您可以将返回数据和执行业务流程的方法转换为查询和命令处理程序。假设你有以下方法:
List<Customer> GetCustomers(string country, string orderBy)
{
...
}
void CreateInvoice(int customerId, decimal amount)
{
...
}
void CreateCustomer(string name, string address)
{
...
}
现在,这些方法不符合接口,所以你不能提取一个。但是,您可以将它们更改为查询和命令模式:
接口:
接口 IQueryHandler
{
TResult 句柄(TQuery 查询);
}
interface ICommandHandler<T>
{
Handle(T command);
}
现在你可以改变你的类,让它们实现这个接口:
class GetCustomersHandler : IQueryHandler<CustomerQuery, List<Customer>>
{
List<Customer> Handle(CustomerQuery query)
{
// CustomerQuery is a simple message type class which contains country and orderby
// just as in the original method, but now packed up in a 'message'
}
}
class CreateInvoiceHandler : ICommandHandler<CreateInvoice>
{
public void Handle(CreateInvoice command)
{
// CreateInvoice is a simple message type class which contains customerId and amount
// just as in the original method, but now packed up in a 'message'
}
}
当你有这个时,你可以创建一个实现日志记录但包装(装饰)底层类的记录器类:
class QueryExceptionHandler<TQuery, TResult> : IQueryHandler<TQuery, TResult>
{
IQueryHandler<TQuery, TResult> _innerQueryHandler;
public QueryLogHandler(IQueryHandler<TQuery, TResult> innerQueryHandler)
{
_innerQueryHandler = innerQueryHandler;
}
TResult Handle(TQuery query)
{
try
{
var result = _innerQueryHandler.Handle(query);
}
catch(Exception ex)
{
// Deal with exception here
}
}
}
当你想使用它时,你可以像这样(从 UI 代码)实例化它。
IQueryHandler<CustomerQuery, List<Customer>> handler =
new QueryExceptionHandler<CustomerQuery, List<Customer>>(new GetCustomersHandler());
var customers = handler.Handle(new CustomerQuery {Country = "us", OrderBy = "Name"});
当然,这个 queryExceptionHandler 也可以被其他处理程序重用(示例):
IQueryHandler<InvoiceQuery, List<Invoice>> handler =
new QueryExceptionHandler<InvoiceQuery, List<Invoice>>(new GetCInvoicesHandler());
var invoices= handler.Handle(new InvoiceQuery {MinAmount= 100});
现在异常处理在一个类中完成,您的所有其他类都不需要为它烦恼。相同的想法可以应用于业务操作(命令端)。
除此之外,在这种情况下,我只是添加了一层用于异常处理。您也可以将异常处理程序包装在记录器中,从而在彼此之上构建各种装饰器。这样你就可以创建一个用于日志记录的类,一个用于异常处理,一个用于...
它不仅允许您将该行为与实际类分开,而且还允许您根据需要为每个不同的处理程序自定义它(包装带有异常和日志记录的客户处理程序以及仅在日志记录中的发票处理程序例如处理程序)
像上面的例子那样构建你的处理程序非常麻烦(尤其是当你开始添加多个装饰器时),但这只是为了向你展示它们是如何协同工作的。
为此使用依赖注入会更好。您可以进行手动 DI,一种功能性方法(参见 Greg Young 的视频)或使用 DI 容器。
我知道它看起来像一个非常复杂的示例,但是您很快就会注意到,一旦您设置了这个小结构,它实际上就很容易使用。您可以参考我的文章,其中还可以看到使用 DI 容器的解决方案。