ExceptionHelper 是自定义的异常处理帮助类,覆盖了个别异常的处理,如果是没有覆盖的异常,我们可能需要记录日志,并告诉客户出现了什么异常。相似地,Redeem 方法也要做相同的处理,此处省略。
此时,我们已经实现了所有非功能需求:logging,防御性编程,事务,重试,和异常处理。将这些处理横切关注点的代码添加到原始的Accrue 和Redeem 方法中使得它们膨胀成巨大的方法。现在代码可以去生产环境(或更可能去QA/预发布环境),但是这代码太糟糕了!
你可能在想这个描述有点过了,并不是所有的横切关注点都是必须的,是的,你可能大多数情况只需要一两个横切关注点,一些关注点可以移到数据层或UI层。但这里要说明的道理是横切关注点可以使你的代码变杂乱,使得代码更难阅读、维护和调试。
不使用AOP重构
是时候整理下代码了,因为Accrue 和Redeem 方法中有很多重复代码,我们可以把这些代码放到它们自己的类或方法中。一种选择是将所有的非功能关注点重构到静态方法中,这是个馊主意,因为这会将业务逻辑紧耦合到非功能关注点代码中,虽然使方法看上去更短更可读了,但仍然留下了方法做的事情太多的问题。你也可以使用DI策略,将所有的logging,防御性编程和其他服务传给LoyaltyAccrualService 和LoyaltyRedemptionService 的构造函数:
public class LoyalRedemptionServiceRefactored:ILoyaltyRedemptionService
{
private readonly ILoyaltyDataService _loyaltyDataService;
private readonly IExceptionHandler _exceptionHandler;//异常处理接口
private readonly ITransactionManager _transactionManager;//事务管理者
public LoyalRedemptionServiceRefactored(ILoyaltyDataService loyaltyDataService, IExceptionHandler exceptionHandler,
ITransactionManager transactionManager)
{
_loyaltyDataService = loyaltyDataService;
_exceptionHandler = exceptionHandler;//通过依赖注入传入
_transactionManager = transactionManager;
}
public void Redeem(Invoice invoice, int numberOfDays)
{
//防御性编程
if (invoice==null)
{
throw new Exception("Invoice为null了!");
}
if (numberOfDays<=0)
{
throw new Exception("numberOfDays不能小于1!");
}
//logging
Console.WriteLine("Redeem: {0}", DateTime.Now);
Console.WriteLine("Invoice: {0}", invoice.Id);
_exceptionHandler.Wrapper(() =>
{
_transactionManager.Wrapper(() =>
{
var pointsPerDay = 10;
if (invoice.Vehicle.Size>=Size.Luxury)
{
pointsPerDay = 15;
}
var totalPoints = numberOfDays*pointsPerDay;
_loyaltyDataService.SubstractPoints(invoice.Customer.Id,totalPoints);
invoice.Discount = numberOfDays*invoice.CostPerDay;
// logging
Console.WriteLine("Redeem complete: {0}",DateTime.Now);
});
});
}
}
上面是重构过的版本,IExceptionHandler 等的代码没有贴出来,请查看源码,这个版本比之前的好多了。我将异常处理代码和事务/重试代码分别放到了IExceptionHandler 和ITransactionManager 中,这种设计有它的优势,一是它把那些代码段放到了他们自己的类中,以后可以重用;二是通过减少了横切关注点的噪音使得代码阅读更容易。
当然,Accrue 方法也可以重构成这样,此处略过。重构之后,代码和最原始的状态差不多了。但是构造函数好像太庞大了,也就是依赖太多了,实际上,这里可以优化一下,往下看。
(编辑:成都站长网)
【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!
|