先来重构一个简单的横切关注点:logging。当方法调用时,会记录方法名和时间戳。创建一个日志切面类,继承自OnMethodBoundaryAspect ,它允许我们在方法的边界插入代码:
[Serializable]
public class LoggingAspect:OnMethodBoundaryAspect
{
public override void OnEntry(MethodExecutionArgs args)
{
Console.WriteLine("{0}:{1}",args.Method.Name,DateTime.Now);
}
public override void OnSuccess(MethodExecutionArgs args)
{
Console.WriteLine("{0} complete:{1}",args.Method.Name,DateTime.Now);
}
}
注意,我们可以通过MethodExecutionArgs 参数获得方法名,因此,这个切面可以c重复使用,可给Accure 和Redeem 方法使用:
public class LoyaltyAccrualService:ILoyaltyAccrualService
{
[LoggingAspect]
public void Accrue(RentalAgreement agreement)
{
//...
}
}
public class LoyalRedemptionService:ILoyaltyRedemptionService
{
[LoggingAspect]
public void Redeem(Invoice invoice, int numberOfDays)
{
//...
}
}
现在就可以从这些方法中移除logging代码了。除此之外,我们还没有打印传入参数的Id,比如Customer.Id 。有了Postsharp,我们可以取到所有的传入参数,但为了取到Id,必须还得做点事情。
public override void OnEntry(MethodExecutionArgs args)
{
Console.WriteLine("{0}:{1}",args.Method.Name,DateTime.Now);
foreach (var argument in args.Arguments)//遍历方法的参数
{
if (argument.GetType()==typeof(RentalAgreement))
{
Console.WriteLine("Customer:{0}", ((RentalAgreement)argument).Customer.Id);
Console.WriteLine("Vehicle:{0}", ((RentalAgreement)argument).Vehicle.Id);
}
if (argument.GetType()==typeof(Invoice))
{
Console.WriteLine("Invoice:{0}",((Invoice)argument).Id);
}
}
}
就这个例子来说,这样没问题了,但是对于一个大一点的应用,可能会有几十个甚至几百个不同的类型,如果需求是记录实体Id和信息,那么可以在实体上使用一个公共接口(或基类)。比如,如果Invoice 和RentalAgreement 都实现了ILoggable 接口,该接口具有一个方法string LogInfo() ,代码可以这样写:
public override void OnEntry(MethodExecutionArgs args)
{
Console.WriteLine("{0}:{1}",args.Method.Name,DateTime.Now);
foreach (var argument in args.Arguments)//遍历方法的参数
{
if (argument!=null)
{
if (typeof(ILoggable).IsAssignableFrom(argument.GetType()))
{
Console.WriteLine((ILoggable)argument.LogInfo());
}
}
}
}
现在Accure 和Redeem 方法开始收缩了,因为我们将logging功能移到了它自己的类日志切面中去了。
重构防御性编程
下面还是使用OnMethodBoundaryAspect 基类重构防御性编程,确保没有参数为null,以及所有的int参数不为0或负数:
[Serializable]
public class DefensiveProgramming:OnMethodBoundaryAspect
{
public override void OnEntry(MethodExecutionArgs args)
{
var parameters = args.Method.GetParameters();//获取形参
var arguments = args.Arguments;//获取实参
for (int i = 0; i < arguments.Count; i++)
{
if (arguments[i]==null)
{
throw new ArgumentNullException(parameters[i].Name);
}
if (arguments[i] is int&&(int)arguments[i]<=0)
{
throw new ArgumentException("参数非法",parameters[i].Name);
}
}
}
}
首先检查实参是否为null,之后再判断参数是否是整型,并且是否合法。如果不处理这些事情,非法值会使得程序崩溃,但这里处理之后我们可以看到崩溃的确定原因(ArgumentNullException或ArgumentException 的异常信息)。
同时,这个类没有直接耦合任何参数类型或服务类,这意味着可以重复使用在多个服务中。
[LoggingAspect]
[DefensiveProgramming]
public void Accrue(RentalAgreement agreement)
{
//...略
}
[LoggingAspect]
[DefensiveProgramming]
public void Redeem(Invoice invoice, int numberOfDays)
{
//...
}
(编辑:成都站长网)
【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!
|