使用装饰器模式分离职责
我们因为安全、调试或是其他的什么原因,经常需要在我们的程序中插入记录日志的代码,在MS 的 Enterprise Library中, 也提供了日志程序块,由此可见,日志对于任何一个系统都是很重要的。
通常,我们记录的做法是这样的
{
public virtual void DoSomeWork ()
{
try
{
//一些处理业务的代码
// 写操作成功日志
}
catch
{
// 写操作失败日志
throw;
}
}
}
当然,假设我们使用工厂来创建业务对象。
{
public static Business CreateBusiness(bool needLog)
{
return new Business(needLog);
}
}
调用代码
biz. DoSomeWork ();
这些代码工作的很好,但是我们不难发现,我们写日志的代码夹杂在业务逻辑代码之中,而且,我们为了记录业务执行失败的情况,甚至在其中插入了本来可能不必要的异常处理—try/catch块。这使我们的业务逻辑代码变得臃肿而且难以维护。
记住 Fowler 告诉我们的:重构以使用模式。经过一段时间的冥思苦想,我发现 Decorator 模式正是用于解决此问题。重构的目的是将日志记录代码从业务逻辑代码中分离,重构步骤如下:
1、 新建一个 装饰类 ,从 Business 继承,并覆写其中的DoSomeWork 方法。
2、 将 异常处理和日志记录移至 派生类 中,使派生类中覆写的方法调用基类中的方法。
3、 更改工厂,使其返回派生类的实例。
重构以后的代码
业务类:
{
public virtual void DoSomeWork()
{
//一些处理业务的代码
}
}
业务的装饰类
{
private Business biz;
public LoggedBusiness(Business biz)
{
this.biz = biz;
}
public override void DoSomeWork()
{
try
{
biz.DoSomeWork();
// 写成功日志
}
catch
{
// 写失败日志
throw;
}
}
}
工厂
{
Business biz = new Business();
if( needLog )
return new LoggedBusiness(biz);
else
return biz ;
}
工厂的使用者,根本没有意识到,他所使用的对象已经悄悄的发生了改变。这也正是 装饰器 模式的优点 --- 对象的使用者根本不知道他所使用的对象已经被装饰了,但装饰器已经开始工作了。另外,我们可以在运行的时候决定是否使用 装饰类,只要稍稍修改工厂--在工厂中根据上下文判断是返回装饰类还是原始类的实例--就可以了。我们可以对对象使用多重装饰,以使其具有多重职责,这一点也很有用。
个人认为,装饰器的作用在于通过扩展的方式把附加的职责添加到原始对象上,本文介绍的只是它的一个小小的应用。
也有人认为,凡是需要用到装饰器模式的地方,都是需要三段话都是需要使用AOP的地方。AOP带来了代码的优雅,然而使用AOP却是有代价的,效率的或者是其他的代价。