A Taste of AOP from Solving Problems with OOP and Design Patterns (Part I)

来源:百度文库 编辑:神马文学网 时间:2024/04/27 18:53:23
A Taste of AOP from Solving Problems with OOP and Design Patterns (Part I)
2004年2月2日 3:42
在展开问题之前我们首先设定一个例子,在这个示例中我将使用尽可能简单的逻辑实现所有功能需求,这将更突出我们所要解决的核心问题。例子是一个简单计算器类:
public class Calculator
{
public int Add(int x, int y) { return x + y; }
}
测试代码如下(你可以使用NUnit与我们一起完成对这个例子的研究):
public void Test()
{
Calculator calculator = new Calculator();
Assert.IsNotNull(calculator);
Assert.AreEqual(5, calculator.Add(2,3));
}
这个类再简单不过了,不过若你将它想象为一个可能更复杂的业务处理类的时候,你将面临除了核心功能实现之外的更多处理细节,比如说:权限控制、审计日志、性能监测、缓冲处理、事务环境等等。为简单起见,我们首先为该类增加记录日志的功能,该功能要求将对每个方法的调用和处理结果输出到Console中,如下:
public class Calculator
{
public int Add(int x, int y)
{
Console.Write("Add({0},{1})", x, y);
int result = x + y;
Console.WriteLine(" = {0}", result);
return result;
}
}
再简单不过了,对吧?现在我们需要为该方法实现性能监测,如下:
public class Calculator
{
public int Add(int x, int y)
{
Console.Write("Add({0},{1})", x, y);
DateTime startTime = PreciseTimer.Now;
int result = x + y;
TimeSpan elapsedTime = PreciseTimer.ElapsedSince(start);
Console.Write(" [{0}] ", elapsedTime);
Console.WriteLine(" = {0}", result);
return result;
}
}
这里暂时不要去管PreciseTimer这个类,它只是一个用来计时的工具而已,跟我们的核心问题没什么大关系。此时你已经感觉到,虽然我们实现了所需的功能,但是在一个方法中堆叠了处理各类事宜(Aspect! I heard you! :)的不同代码。虽然在这个简单例子中不会感觉有什么不爽,但是请你想象一下如果我们将为该类添加第二个方法时会发生什么事情:
public class Calculator
{
public int Add(int x, int y)
{
Console.Write("Add({0},{1})", x, y);
DateTime startTime = PreciseTimer.Now;
int result = x + y;
TimeSpan elapsedTime = PreciseTimer.ElapsedSince(start);
Console.Write(" [{0}] ", elapsedTime);
Console.WriteLine(" = {0}", result);
return result;
}
public int Subtract(int x, int y)
{
Console.Write("Subtract({0},{1})", x, y);
DateTime startTime = PreciseTimer.Now;
int result = x - y;
TimeSpan elapsedTime = PreciseTimer.ElapsedSince(start);
Console.Write(" [{0}] ", elapsedTime);
Console.WriteLine(" = {0}", result);
return result;
}
}
此时的单元测试代码补充如下:
public void Test()
{
ICalculator calculator = new Calculator();
Assert.IsNotNull(calculator);
Assert.AreEqual(5, calculator.Add(2,3));
Assert.AreEqual(5, calculator.Subtract(8,3));
}
在两个方法中已经明显出现重复代码了,这可不是一个好的smell——想想一下如果我们的计算器有10个方法呢?如果我们还有类似于计算器类的另外数十个类呢?如果我们还有更多的方法级功能要实现呢(权限控制、事务管理……)?在企业级应用开发中,这可是一个经常会遇的问题。为清楚起见,我们将问题分解成两部分,首要的问题是代码职责混淆,其次则是同样的代码逻辑反复多次——这些问题都将导致开发管理、代码编写与维护的各种困难。
为了解决代码职责混淆的问题,我们首先考虑的是使用DECORATOR设计模式将不同职责的代码分解到若干个DECORATOR中,并继而使用一种对象构造模式(如ABSTRACT FACTORY或FACTORY METHOD或BUILDER等等,都可以)在运行时将DECORATOR与核心实现组装起来。为了实现DECORATOR模式,我们首先需要使用Extract Interface的重构技术提取接口,如下:
public interface ICalculator
{
int Add(int x, int y);
int Subtract(int x, int y);
}
public class Calculator: ICalculator
{
public int Add(int x, int y)
{
Console.Write("Add({0},{1})", x, y);
DateTime startTime = PreciseTimer.Now;
int result = x + y;
TimeSpan elapsedTime = PreciseTimer.ElapsedSince(start);
Console.Write(" [{0}] ", elapsedTime);
Console.WriteLine(" = {0}", result);
return result;
}
public int Subtract(int x, int y)
{
Console.Write("Subtract({0},{1})", x, y);
DateTime startTime = PreciseTimer.Now;
int result = x - y;
TimeSpan elapsedTime = PreciseTimer.ElapsedSince(start);
Console.Write(" [{0}] ", elapsedTime);
Console.WriteLine(" = {0}", result);
return result;
}
}
测试代码相应修改如下:
public void Test()
{
ICalculator calculator = new Calculator();
Assert.IsNotNull(calculator);
Assert.AreEqual(5, calculator.Add(2,3));
Assert.AreEqual(5, calculator.Subtract(8,3));
}
运行测试代码可以确信现在Calculator的行为未受到任何影响(这是正确使用重构技术的重要标志噢),那么我们开始剥离负责性能监测职责的代码并将其作为一个DECORATOR实现:
public class Calculator: ICalculator
{
public int Add(int x, int y)
{
Console.Write("Add({0},{1})", x, y);
DateTime startTime = PreciseTimer.Now;
int result = x + y;
TimeSpan elapsedTime = PreciseTimer.ElapsedSince(start);
Console.Write(" [{0}] ", elapsedTime);
Console.WriteLine(" = {0}", result);
return result;
}
public int Subtract(int x, int y)
{
Console.Write("Subtract({0},{1})", x, y);
DateTime startTime = PreciseTimer.Now;
int result = x - y;
TimeSpan elapsedTime = PreciseTimer.ElapsedSince(start);
Console.Write(" [{0}] ", elapsedTime);
Console.WriteLine(" = {0}", result);
return result;
}
}
public class CalculatorTimer: ICalculator
{
public int Add(int x, int y)
{
DateTime startTime = PreciseTimer.Now;
int result = Decoratee.Add(x, y);
TimeSpan elapsedTime = PreciseTimer.ElapsedSince(start);
return result;
}
public int Subtract(int x, int y)
{
DateTime startTime = PreciseTimer.Now;
int result = Decoratee.Subtract(x, y);
TimeSpan elapsedTime = PreciseTimer.ElapsedSince(start);
return result;
}
}
可以看到,在负责性能监测(在这个简单例子中我们还不如就叫它timer吧:)的DECORATOR中,代码只关注计时的功能,而将核心的工作交由decoratee(自造词,别介意——caller/callee、tester/testee、assigner/assignee、bomber/bombee……:)去处理。这里的decoratee也是一个ICalculator类型的接口引用,我们需要在构造decorator的时候为其设置好decoratee,这个工作将在对象工厂中实现。这里我一次到位的为所有可能的DECORATOR实现一个基类,该类实现一个新的接口ICalculatorDecorator,在这个接口中我们声明一个支持读写decoratee的语义,如下:
public interface ICalculatorDecorator
{
void InitDecorator(ICalculator decoratee);
ICalculator Decoratee { get; }
}
然后我们为所有的Decorator提供该接口的一个基本实现:
public abstract class CalculatorDecorator: ICalculatorDecorator
{
private ICalculator decoratee;
void ICalculatorDecorator.InitDecorator(ICalculator decoratee)
{
this.decoratee = decoratee;
}
// FIXED: to use implicit interface implementation instead explicit way
// so that derived classes could access this property. Thanks greatqn!
ICalculator ICalculatorDecorator.Decoratee
public ICalculator Decoratee
{
get { return this.decoratee; }
}
}
再由此派生出之前的CalculatorTimer:
public class CalculatorTimer: CalculatorDecorator, ICalculator
{
public int Add(int x, int y)
{
DateTime startTime = PreciseTimer.Now;
int result = Decoratee.Add(x, y);
TimeSpan elapsedTime = PreciseTimer.ElapsedSince(start);
return result;
}
public int Subtract(int x, int y)
{
DateTime startTime = PreciseTimer.Now;
int result = Decoratee.Subtract(x, y);
TimeSpan elapsedTime = PreciseTimer.ElapsedSince(start);
return result;
}
}
为了将这个负责性能监测职能的DECORATOR与其被装饰者(decoratee)组合起来,我们需要一个对象工厂类(它同时可以将调用代码逻辑与对象创建逻辑解耦):
public class CalculatorFactory
{
private CalculatorFactory() {}
private static Type[] GetObjectGraph()
{
ArrayList objectGraphTypes = new ArrayList();
// Add decoratee as 1st type to be created
objectGraphTypes.Add(typeof(Calculator));
// and then add all decorators
objectGraphTypes.Add(typeof(CalculatorTimer));
return (Type[])objectGraphTypes.ToArray(typeof(Type));
}
public static ICalculator CreateInstance()
{
Type[] objectGraphTypes = GetObjectGraph();
ICalculator result = null;
foreach (Type calcType in objectGraphTypes)
{
ICalculator calcImpl = (ICalculator)Activator.CreateInstance(calcType);
if (calcImpl is ICalculatorDecorator)
{
((ICalculatorDecorator)calcImpl).InitDecorator(result);
}
result = calcImpl;
}
return result;
}
}
相应修改单元测试使其使用这个工厂的CreateInstance()方法(这实际是设计模式中所谓的FACTORY METHOD),如下:
public void Test()
{
ICalculator calculator = CalculatorFactory.CreateInstance();
Assert.IsNotNull(calculator);
Assert.AreEqual(5, calculator.Add(2,3));
Assert.AreEqual(5, calculator.Subtract(8,3));
}
在这个类里面我们提供了一个静态方法CreateInstance()用来返回ICalculator的一个实现。在这个实现逻辑中,我们首先构造一个对象组装图(object graph),其实就是一个类型列表,按照从内到外的顺序依次添加若干ICalculator或ICalculatorDecorator的实现类型。接下来,一个foreach循环按照构造列表依次创建每一个类型,并在创建后判断新创建的类型是否是一个ICalculatorDecorator,如果是的话,我们将已然创建好的最后一个ICalculator实现(即result)作为decoratee赋予它以实现对象组合。循环完成时,result所引用的ICalculator实现就是最外面的一个decorator,而整个调用链其实是通过每一个DECORATOR类的方法中的Decoratee.XXX()来构成的。
有了这个改进的对象结构,我们可以很容易的添加新的DECORATOR为对象增加新的职责,如将之前的日志记录逻辑实现如下:
public class CalculatorLogger: CalculatorDecorator, ICalculator
{
public int Add(int x, int y)
{
Console.Write("Add({0}, {1})", x, y);
int result = Decoratee.Add(x, y);
Console.WriteLine(" = {0}", result);
return result;
}
public int Subtract(int x, int y)
{
Console.Write("Subtract({0}, {1})", x, y);
int result = Decoratee.Subtract(x, y);
Console.WriteLine(" = {0}", result);
return result;
}
}
接下来只要在对象工厂的CreateInstance()方法中将其加入对象构造图中即可:
public class CalculatorFactory
{

private static Type[] GetObjectGraph()
{
ArrayList objectGraphTypes = new ArrayList();
// Add decoratee as 1st type to be created
objectGraphTypes.Add(typeof(Calculator));
// and then add all decorators
objectGraphTypes.Add(typeof(CalculatorTimer));
objectGraphTypes.Add(typeof(CalculatorLogger));
return (Type[])objectGraphTypes.ToArray(typeof(Type));
}
public static ICalculator CreateInstance() …
}
不过,以我有限的设计经验来看,让所有的DECORATOR显式去调用Decoratee的方法以形成对象关系链是繁琐和易出错的(不过也是效率最好的)。因为如果某一个方法实现没有去调用Decoratee的相应方法的话,整个对象链将被切断。同时我们还发现,随着组件接口的日益复杂,接口中方法的数目越来越多,因此对于每一个新增的DECORATOR而言都需要实现所有这些接口方法,这对于一些并非施加于所有接口方法的DECORATOR而言就显得有些累赘了(如仅对少数方法有意义的缓冲策略DECORATOR等等)。这些现象都提示我们可以使用TEMPLATE METHOD设计模式来为所有的DECORATOR提供一个统一的、灵活的基础实现逻辑,如下:
public abstract class CalculatorDecorator: ICalculatorDecorator, ICalculator
{
private ICalculator decoratee;
void ICalculatorDecorator.InitDecorator(ICalculator decoratee)
{
this.decoratee = decoratee;
}
public ICalculator Decoratee
{
get { return this.decoratee; }
}
int ICalculator.Add(int x, int y)
{
DoPreAdd(ref x, ref y);
int result = decoratee.Add(x, y);
DoPostAdd(x, y, ref result);
return result;
}
int ICalculator.Subtract(int x, int y)
{
DoPreSubtract(ref x, ref y);
int result = decoratee.Subtract(x, y);
DoPostSubtract(x, y, ref result);
return result;
}
protected virtual void DoPreAdd(ref int x, ref int y) {}
protected virtual void DoPostAdd(int x, int y, ref result) {}
protected virtual void DoPreSubtract(ref int x, ref int y) {}
protected virtual void DePostSubtract(int x, int y, ref result) {}
}
在这个基于TEMPLATE METHODS设计模式的DECORATOR基类中,我们显式的实现了ICalculator接口所有方法的模板逻辑,模板中可变的部分委托给若干protected virtual的模板方法实现,这些模板方法继而可被派生类可选择的override(因为是virtual的)。当然,如果你希望所有的DECORATOR一定要提供某一个环节的模板方法实现,你也可以使用protected abstract来修饰这个模板方法,这样一来派生类就必须要实现这个方法才能够通过编译了。另外,虽然我们在基类上使用了显式接口实现,但是派生类仍然可以重新提供相关接口的实现,这种灵活度足以满足各种应用场合了(参见C#语言规范之13.4.1和13.4.4)。
还有一个问题需要解决,即DECORATOR之间的相互协作在目前的设计中是不可以完成的。为什么要DECORATOR之间要协作?举个简单例子,如果我们需要在Logger中顺便记录方法执行的时间,而这个时间是由Timer来测定的话,我们就需要在Logger的实现中取得由Timer记录下来的时长信息(当然,这同时要求Logger位于Timer的外层,这样当Timer返回后Logger才可能获取到Timer记录的信息)。为了达到这个目的,需要为所有组成对象组合的对象实例引入一个公共的信息容器(比如一个使用Hashtable实现的数据字典),不妨称其为Context。我们希望所有的DECORATOR都可以取得这个共享的容器,因此首先我们扩充ICalculatorDecorator接口及其实现:
public interface ICalculatorDecorator
{
void InitDecorator(ICalculator decoratee, IDictionary context);
ICalculator Decoratee { get; }
IDictionary Context { get; }
}
public abstract class CalcuatorDecorator: ICalculatorDecorator
{
private ICalculator decoratee;
private IDictionary context;
public void InitDecorator(ICalculator decoratee, IDictionary context)
{
this.decoratee = decoratee;
this.context = context;
}
public ICalculator Decoratee
{
get { return this.decoratee; }
}
public IDictionary Context
{
get { return this.context; }
}
}
接下来,我们在对象工厂创建对象的时候为复合对象创建并设置context:
public class CalculatorFactory
{

public static ICalculator CreateInstance()
{
Type[] objectGraphTypes = GetObjectGraph();
ICalculator result = null;
IDictionary context = new Hashtable();
foreach (Type calcType in objectGraphTypes)
{
ICalculator calcImpl = (ICalculator)Activator.CreateInstance(calcType);
if (calcImpl is ICalculatorDecorator)
{
((ICalculatorDecorator)calcImpl).InitDecorator(result, context);
}
result = calcImpl;
}
return result;
}
}
这样,在DECORATOR(也即CalculatorDecorator的派生类)的内部就可以使用Context属性访问整个对象组合中各个DECORATOR的共享存储环境:
public class CalculatorTimer: CalculatorDecorator, ICalculator
{
public int Add(int x, int y)
{
DateTime startTime = PreciseTimer.Now;
int result = Decoratee.Add(x, y);
TimeSpan elapsedTime = PreciseTimer.ElapsedSince(start);
Context["Add.ElapsedTime"] = elapsedTime;
return result;
}
public int Subtract(int x, int y)
{
DateTime startTime = PreciseTimer.Now;
int result = Decoratee.Subtract(x, y);
TimeSpan elapsedTime = PreciseTimer.ElapsedSince(start);
Context["Subtract.ElapsedTime"] = elapsedTime;
return result;
}
}
public class CalculatorLogger: CalculatorDecorator, ICalculator
{
public int Add(int x, int y)
{
Console.Write("Add({0}, {1})", x, y);
int result = Decoratee.Add(x, y);
Console.WriteLine(" = {0} {1}", result, Context["Add.ElapsedTime"]);
return result;
}
public int Subtract(int x, int y)
{
Console.Write("Subtract({0}, {1})", x, y);
int result = Decoratee.Subtract(x, y);
Console.WriteLine(" = {0} {1}", result, Context["Subtract.ElapsedTime"]);
return result;
}
}
本例进展至此已经形成了一定的对象结构,让我们小结一下吧。首先,我们提出了一个问题,即在企业系统开发中因为既要满足功能性需求,也需要照顾很多不同方面的非功能性需求,这使得对象方法的实现变得越来越复杂、繁琐、僵硬和难以维护(虽然从执行效率的角度看还是最优化的);接下来我们引入对象结构设计模式DECORATOR将对象必须实现的各方面代码分散到以接口为基础的核心实现对象和若干修饰对象,并进而引入对象创建设计模式将这些对象的构造过程封装起来,通过接口保持与调用代码的松散耦合;最后,我们对已经形成的对象结构进行小的修正与扩充,使其允许各个DECORATOR通过一个共享的Context相互通信。
经过这个设计过程,我们最终形成了一个比起最初模型而言更易于扩展的、松散耦合的、较易维护的对象模型,然而我们也面临一个问题:如果我们有很多这样的业务组件都需要同样的一些DECORATOR的话,我们仍然会面临很多重复代码,而且这些代码在面向对象的范畴内是无法消除的。什么意思?以Logger为例,如果给几十个组件的数百个方法都去实现logging的DECORATOR,无疑仍然是个巨大的开发和维护工作。为什么不可以通过面向对象的技术消除?因为我们希望消除的重复是在方法的层次上,而方法已经是对象内部封装的范畴,很难为不同的类型的每个方法提供一个统一的实现(如果要有也应该在所有对象的一个公共基类中——Object?显然进了死胡同……)。那么这个问题该如何解决呢(尤其是在.NET的环境中)?我将在本文的下篇中结合CLR中提供的相关机制探讨在.NET环境下进行AOP(Aspect-Oriented Programming,面向方面的编程思想)编码的相关技术,包括CLR中的TranspantProxy/RealProxy、MarshalByRefObject/ContextBoundObject、Context Attribute/Context Property/IMessageSink等等。
如果你有与此话题相关的开发经验或想法,欢迎你写在评论中与我分享。本文难免有不妥之处希望个中高手予以指教,谢谢!:)
反馈
# 回复: A Taste of AOP from Solving Problems with OOP and Design Patterns (Part I)
2004-2-2 10:10 bykaneboy
我见过的最活生生的Decorator模式的好示范,不过用来实现AOP,呜呜,如果当初写代码的时候没有考虑到这个要求,后面再重构代码的工作量是巨大的,偶这样的懒人估计只有哭了。
如果我面对一个先前没有考虑AOP的类,就定义两个delegate,比如BeforeAdd和AfterAdd,然后在Add()方法中执行这两个委托指向的方法。如果其他地方要加上一个额外的操作,就写一个delegate方法,用+=串接到那个类中定义的delegate上面去。嗯,就是类似.net里面处理事件的那种方式。没有JGTM2004的方式完善,但是好像实现挺快的,嘻嘻...
# A Taste of AOP from Solving Problems with OOP and Design Patterns (Part I)
2004-2-2 19:15 byJGTM‘2004 .NET Blog
# 回复: A Taste of AOP from Solving Problems with OOP and Design Patterns (Part I)
2004-2-2 19:48 byJGTM‘2004 [MVP]
@kaneboy:如果当初写代码的时候没有考虑到这个要求,后面再重构代码的工作量是巨大的,偶这样的懒人估计只有哭了。
在文章的下半部分将使用.NET Remoting基础架构中提供的基于TP/RP的方法拦截机制来处理同样的需求,你将可以发现该方法明显的好处,但我也正在总结其明显的弊端(主要是本地对象的性能问题)。
# 回复: A Taste of AOP from Solving Problems with OOP and Design Patterns (Part I)
2004-2-2 19:55 byJGTM‘2004 [MVP]
@kaneboy:
使用delegate有些类似于使用TEMPLATE METHODS模式,只不过.NET的多播委托机制已经在内部形成了方法调用链,因此你省去了自己组装对象的麻烦。
不过麻烦将转变为定义两倍于方法数目的委托类型,而且最要命的是无论用何种方法,只要还是面向对象的结构,就不能够提取这些重复逻辑——因为我们要提取的逻辑正是对象试图封装的逻辑。这也就是为什么AOP的思想被提出来,且放在与OOP同等的地位上的原因吧。:)
# 回复: A Taste of AOP from Solving Problems with OOP and Design Patterns (Part I)
2004-2-2 20:11 byprogame
不错 但由于我接受java的aop在先
所以我看着这种实现方法简直想哭
.Net下的aop如何我不清楚
等着看下篇了
# 回复: A Taste of AOP from Solving Problems with OOP and Design Patterns (Part I)
2004-2-2 21:44 byjjx
越看到后来,越难受,如果不是为了说明问题的话,真是属于过度设计了
aop .net 实现不多,不知道你是否看过
loop.doom
http://www.dcl.hpi.uni-potsdam.de/cms/research/loom/
里面也有一个例子
# 回复: A Taste of AOP from Solving Problems with OOP and Design Patterns (Part I)
2004-2-2 21:50 byjjx
http://wwwse.fhs-hagenberg.ac.at/se/berufspraktika/2002/se99047/contents/english/aop_net.html
下面有remoting的实现
http://msdn.microsoft.com/msdnmag/issues/02/03/AOP/default.aspx
期待JGTM‘2004 的更好的说明
# 回复: A Taste of AOP from Solving Problems with OOP and Design Patterns (Part I)
2004-2-2 23:33 bymoslem
现在还不是太懂,Add Favorite 以后慢慢看 ...
# 回复: A Taste of AOP from Solving Problems with OOP and Design Patterns (Part I)
2004-2-3 2:47 byJGTM‘2004 [MVP]
谢谢各位的反馈,我的目的算达到了。;)
@jjx:
由于本文中写到的方法正是在我们的当前项目中所使用的,而它也是从最简单的模型根据开发需求和团队组织、开发规划而逐步演变而来的设计,所以还算是一个有针对性地设计(当然你不能拿文中这般简单的例子来衡量了,我不是一再的说想象如果业务组件数量众多、功能接口方法繁杂时候的情形嘛:)其中有一个明显的例子,很多设计指南中也一再强调,就是业务组件关于安全规则方面的代码最好不要与业务组件的核心实现代码混在一起;当然以此类推,非核心功能代码(如审计、异常管理、性能计数等等)都应该是松散的内聚在功能核心的外围了(最好还是即插即用、可以在部署以后动态配置的——我们的项目中也有类似的操作性需求)。再加上从源码控制、人员分工协作的角度看,本文中的采用的设计策略还是有一定实用场合的。
至于本文的下篇,确实是用到基于.NET Remoting Infrastructure的相关技术。不过看到国内这方面文章很少,又加上自己最近研习有些心得体会,所以写出来与大家分享,也是检验自己理解的程度,还望你多多指教啦!:)
@progame:
关于Java AOP的实现,我了解得甚少,只是知道有个AspectJ,大概是使用编译技术静态植入代码吧?在.NET中也有类似的技术,但确实太底层了,很难在一般的企业级应用中采用(至少在相关技术与工具成熟之前)。因此我们希望利用.NET自身架构中的一些可扩展机制实现类似的功能,并以此阐述这种机制的重要价值。虽然现在.NET在实现该机制中尚存在一些硬伤(如较低的性能和有限的灵活性),但是不可否认的是AOP的思想将是继OOP以来对软件开发最大的贡献,我们也希望透过自身有限的影响力引发更多有价值的想法——为什么永远都是外国人发明这个发明那个呢?为什么我们不能为这个产业做出一些有益的贡献呢?:)
# 回复: A Taste of AOP from Solving Problems with OOP and Design Patterns (Part I)
2004-2-4 15:12 byJGTM‘2004 [MVP]
感谢greatqn网友发现的本文中的一处用法错误,我已经在正文中更正了。谢谢!:)
# 回复: A Taste of AOP from Solving Problems with OOP and Design Patterns (Part I)
2004-2-5 13:33 by
非常不错的文章,有个疑问在此请教一下:
文中提到的“基于TEMPLATE METHODS设计模式的DECORATOR”不是应该重载protected virtual函数,就像这样吗:
public class CalculatorTimer: CalculatorDecorator
{
protected void DoPreAdd(ref int x, ref int y)
{
m_startTime = PreciseTimer.Now;
decoratee.DoPreAdd();
}
protected void DoPostAdd(ref int x, ref int y)
{
decoratee.DoPostAdd();
elapsedTime = PreciseTimer.ElapsedSince(m_startTime);
// Set context attributes if needed.
// ....
}
}
为什么看到您后面的代码还是重载Add和Substract来计算操作的时间呢?
另外,我觉得使用Template Method并不能避免显式调用DECORATEE的方法,因为这个对象链无论如何都是需要程序员来维护的,除非像您说的那样实用delegate,让系统维护这个list。
期待您的下一篇大作,:))
# 回复: A Taste of AOP from Solving Problems with OOP and Design Patterns (Part I)
2004-2-5 14:16 byJGTM‘2004 [MVP]
@ken:
>为什么看到您后面的代码还是重载Add和Substract来计算操作的时间呢?
您说的很对!是我后面的例子举得不好,没有好好支持刚刚上文提出的论点。我正在根据大家的反馈完善本文,使其更加清楚的说明问题和实现机制。谢谢您的意见!:)
> 另外,我觉得使用Template Method并不能避免显式调用DECORATEE的方法,因为这个对象链无论如何都是需要程序员来维护的,除非像您说的那样实用delegate,让系统维护这个list。
这要看您如何应用Template Method这个设计模式了,毕竟这个模式要解决的问题之一就是提供一个一致的执行框架使得派生类只提供可供定制的逻辑步骤。如果派生类只是重载DoPreXXX/DoPostXXX的话,则对象链是可以在基类中被保护起来的。当然,如果你一定需要破坏这个链,还是有很多办法的,比如在派生类重新实现接口,但是否应该这
样去做还是由程序员思考和判断的——这只是一个bias的问题,即:不使用template method的话则必须显式保持调用链;而使用了template method则必须显式打破调用链。您觉得呢?:)
> 期待您的下一篇大作,:))
谢谢!很高兴与大家一起分享我的点滴收获和个中乐趣!:)
# 回复: A Taste of AOP from Solving Problems with OOP and Design Patterns (Part I)
2004-2-19 10:56 byFlier Lu
我也不是很喜欢AspectJ的那种实现思路。但文中这种手工编写包装代码太麻烦了,繁琐易错,对大型项目来说成本过高;使用.NET Remoting的思路则感觉太臃肿,而且受到好些限制,效率方面也欠佳。个人比较欣赏JBoss在实现AOE时采用的配置加动态绑定的技术,而这方面.NET架构有Attributes和System.Reflection.Emit提供强力支持,应该是有发展前途的。模式虽好,变成薄记工作的话,反而会适得其反。
# 回复: A Taste of AOP from Solving Problems with OOP and Design Patterns (Part I)
2004-2-19 11:41 byJGTM‘2004 [MVP]
@Flier Lu:
你说得很对,虽然本文中的解决方法解决了原来软件结构中的问题(主要是低内聚),但是实现成本相当高。在下一篇中,我会描述利用.NET Remoting Infrastructure中的TP/RP特性解决问题的另一条路,不过也正如你所说,效率会有所损失(虽然对于企业级应用来说还算不得是主要矛盾),而且有某些限制。至于说Dynamically Code Emit,暂时我还没有找到比较合理可用的解决方案,不过它提供的性能诱惑仍然是一个动力。在本文的后两篇中,我会给出一个正在我们当前项目中采用的比较折中的解决方案,有机的把两种不同机制整合起来,提供一个企业级项目快速开发松散耦合、高度内聚组件的基础服务——诚然,如果微软能够在AOP这方面给予更大的关注与支持,相信问题会得到更圆满的解决,我们也不用再羡慕那些Java社区中的大块头了。:)
# 回复: A Taste of AOP from Solving Problems with OOP and Design Patterns (Part I)
2004-4-5 10:54 by
强劲!
# 回复: A Taste of AOP from Solving Problems with OOP and Design Patterns (Part I)
2004-7-14 16:32 by
有个疑问,Decorator模式,你这里好像主要是拿来解决代码复用问题。如果Decoratee很多的话,那么Decorator将实现很多的接口;比如一个Calculator有Add(),Substract() ,另外一个DbController有GetData()和SetData(),那么Timer和Log两个类将实现这四个接口,按你的话,你们的运用中有很多的组件,那接口数势必也不少; 这是decorator模式期望的使用方案吗?
# AOP through .Net Remoting Infrastructure
2004-8-17 11:16 byEvan
Ping Back来自:blog.csdn.net
# re: A Taste of AOP from Solving Problems with OOP and Design Patterns (Part I)
2004-12-17 22:30 bywayfarer
今天我照着你的代码,演练了一遍,确实很好。
不过如果Decorator装饰对象太多的话,会有多种组合方式,也就是说,在工厂方法中,对象图的顺序可能会发生变化,个数也有变化。这个时候对应的工厂方法,可能就会有多种了。
# re: A Taste of AOP from Solving Problems with OOP and Design Patterns (Part I)
2005-2-17 11:18 by
不错的技术文章。
LOOM.NET is a static aspect weaver that operates on binary .NET assemblies. The RAPIER-LOOM.NET library is a dynamic aspect weaver.
# re: A Taste of AOP from Solving Problems with OOP and Design Patterns (Part I)
2005-3-7 16:01 by
使用了NHibernate,想采用Aop来处理事务,参考过spring4j对Hibernate的事务处理,但是,Spring4net有Aop但是没有对NHibernate的事务处理。所以,在事务处理上只是使用了截获但是没有对事务上下文的传递。这个问题期待能有好得解决。
在Log上采用了AspectC#,Aspect#比较轻量,使用起来也很简单。但是还是没有支持NHibernate的[suporttype]的支持。
能给歌解决方案吗??
# re: A Taste of AOP from Solving Problems with OOP and Design Patterns (Part I)
2005-9-24 14:45 byTeddy
http://teddyma.cnblogs.com/category/36167.html