概要介绍
笔者认为AspectF是比较不错的.NET下AOP解决方案,当然Spring.NET框架下的AOP功能也很强大非常出色,但这个框架相对来说比较复杂,本篇文章将着重向大家介绍AspectF这个专注于AOP的轻量级的框架的使用。AspectF可以使用一种简单的方式为你的代码添加方面(Aspects),通过使用AspectF,代码将变得干净整洁。你可以在这里获得Aspect框架相关的支持。
如果你熟悉面向方面编程(Aspect Oriented Programming),那么你就应该知道AOP能使得代码的编写更干净清晰,可维护性更高。但AOP在.NET中的实现通常是由第三方框架(Spring.NET)或者硬编码实现,例如通过对IL的操作实现,尽管相比好处大于它带来的复杂性,但是这并不是一个简单的实现AOP的解决方案。下面我们先强调以下AOP的概念。
Aspect Oriented Programming(AOP)
方面(Aspects)是你在编写代码的过程中,出现在代码不同部分里的相同的程序功能片段,例如它可能是一种处理异常的方法,也可以是调用方法时的日志(logging)记录,或者是方法执行时间的记录(timming execution),这些部分很可能出现在程序的不同地方,是一些可以重用的方面。如果你在编码的过程中没有使用任何的AOP框架的话,那么你将重复的编写这些相同的代码,这使得你的代码难以维护。例如你的业务逻辑层需要进行日志记录,错误处理,执行时间需要记录,那么你的代码很可能是这样:
public bool InsertCustomer(string firstName, string lastName, int age)
{
Logger.Writer.WriteLine("Inserting customer data...");
DateTime start = DateTime.Now;
try
{
CustomerData data = new CustomerData();
bool result = data.Insert(firstName, lastName, age, attributes);
if (result == true)
{
Logger.Writer.Write("Successfully inserted customer data in "
+ (DateTime.Now-start).TotalSeconds + " seconds");
}
return result;
}
catch (Exception x)
{
Debug.WriteLine(x.StackTrace);
Logger.Writer.WriteLine(x.StackTrace);
return false;
}
}
上面我们写了一些实际的代码,这个代码片段实现了客户信息的插入操作,同时代码中还有日志记录、异常处理和时间记录的操作,由于这种代码的混合,逻辑上显得很混乱可读性变得很差。但是设想一下,如果要在业务逻辑中添加验证或其它方面的时候,那么随着代码的添加业务逻辑将变得更乱,真是要多乱有多乱。而且当你给程序业务逻辑添加新的方法时,你需要不断的复制粘贴代码,修改这每个业务逻辑层的部分,例如:你需要在业务逻辑中添加一个UpdateCustomer的方法,你就必须在复制粘贴代码一遍,这真是很苦闷的一件事。
再想象一下,对于修改来说,如果我们的项目需要大范围的修改错误处理的方式,那你就必须把所有业务层的方法一个一个的进行修改。然后如果新的需求又来了,要修改时间统计的方式…这过程真是苦不堪言啊。
面向方面编程解决了上述问题,如下边代码所示,看起来是不是很简洁呢?
[Log]
[TimeExecution]
public void InsertCustomerTheCoolway(string firstName, string lastName, int age)
{
CustomerData data = new CustomerData();
data.Insert(firstName, lastName, age, attributes);
}
在AOP中你将把例如logging,timming,Validation这些方面从业务代码中进行分离。如上例所示,你可以通过Attribute解决,这样的代码干净又清晰,这里每一个Attribute代表一个方面。例如:通过添加Logging方面你可以进行日志记录。总之,无论你用了什么AOP框架,它都保证了方面在运行时被编入了你的代码中。
简单的AOP框架AspectF
让我们来了学习一下怎么在.NET环境下利用AspectF这个框架建立C#程序,这个程序将不使用Attribute或者IL操作实现AOP,只用简单地调用类和委托就可以了,而且使用这种方式的程序具备了很高的重用性和可维护性,而最重要的是它很轻量级,只是一个简单的类——AspectF。如下面代码所示,这是AspectF之于上例的应用:
public void InsertCustomerTheEasyWay(string firstName, string lastName, int age)
{
AspectF.Define
.Log(Logger.Writer, "Inserting customer the easy way")
.HowLong(Logger.Writer, "Starting customer insert", "Inserted customer in {1} seconds")
.Do(() =>
{
CustomerData data = new CustomerData();
data.Insert(firstName, lastName, age);
});
}
如上述代码所示,可能从写法上大家会觉得有些奇怪,不过这就是AspectF的编写方式,可能看完后存在一些疑惑,但接下来我们分析后你将发现它确实很容易。首先AspectF.Define()是个静态方法,它返回了AspectF这个对象,这个对象是整个框架的核心。而代码中的Log和HowLong就是我们在外部定义的方面,而Do方法包含的内容就是我们应用这些方面的目标代码,整个代码是从左到右进行方面的调用的,形成了一条方面链。通过AspectF我们就可以在业务逻辑的外部定义方面的代码了,同时使业务更专注。在上述分析之后大家应该有了一个前期的理解了,之后我们将逐步介绍这些方面是如何进行定义的。
在解释Aspects如何定义之前,我们先来看看AspectF的优势:
(1)AspectF使得方面的定义更清晰,如果使用Attribute或者IL操作,这对用户而言是一个黑盒;
(2)使用AspectF可以不用过度考虑性能的损失,因为它只是一个轻量级的类;
(3)你可以向方面传递参数,而其它框架的AOP是不允许这么做的;
(4)AspectF甚至不能称为一个框架,因为它只是一个叫做AspectF的类而已。
如何在你的类中定义Aspects
在这部分我们将介绍如何创建自己的aspects。首先你先为AspectF类创建一个扩展方法。例如我们将针对上例创建一个Log方面,如下面代码所示:
public static AspectF Log(this AspectF aspect, TextWriter logWriter, string Message)
{
return aspect.Combine((work) =>
{
work();
logWriter.Write(DateTime.Now.ToUniversalTime().ToString());
logWriter.Write('\t');
logWriter.Write(Message);
logWriter.Write(Environment.NewLine);
});
}
首先我们看看参数,AspectF指的是针对该调用方法的AspectF对象的本身,而后两个参数则是上例中我们传入的参数。我们再来看看方法内部的结构,你将调用AspectF对象的Combine方法把Log的内容编入AspectF的委托链中,其实在此部分Log的已经委托给了AspectF,当AspectF的Do方法被调用后,那么委托链上定义的方面都将按照从左到右的顺序依次执行。另外大家可能都注意到了work()这个方法调用,work是Action类型的,它表示的是我们的目标代码,对于上例来说就是客户信息插入的代码片段。 说到这里大家应该有了个比较直观的了解了吧。例子可能有些不全面,但是请大家自行阅读AspectF的源码和示例,相信会有很大收获的。
总结
可能有些对AOP方面非常熟悉的人在读完本篇文章后会有些争议,因为AspectF并没有呈现传统的AOP的特征,因为AOP在很对教材中的定义的核心是“关于”(concern)的分离和横截(cross-cutting),例如Spring.NET或者Struts2.0中包含的方法拦截,通过通知(notification)实现AOP。而AspectF确实没有进行横截的方法,但是它却实现了将“关于”的分离,可以说它具有AOP的特性,同时AscpectF的主要目标是创建独立的,脱离外部类库的程序,只需要使用AspectF对象,我们就可以轻松的创建新的方面,使用强类型和构建程序的新特性。
总之AspectF相比框架真的很简单,可能笔者的描述有些一笔带过之嫌,还望见谅多多指点,总之希望大家能喜欢并多多支持,最后祝大家有个好心情!