[设计模式]在CodeDom代码生成中使用Decorator模式实现类型创建
我估计从博客园建站开始,就不断地有文章对设计模式进行讨论了。设计模式被认为是软件工程的基础,是面向对象分析与设计的指南。博客园中不乏大量的优秀文章,针对设计模式中创建型模式、结构型模式以及行为型模式共计23种模式进行讨论,有的文章也自成体系,以不同的角度来分析各种模式的应用场景和动态特性。今天,我也在所有设计模式专家面前班门弄斧一次,冒着被喷的危险,谈谈Decorator模式的具体应用。与大多数其它介绍设计模式的文章相比,本文介绍的案例真正来自实践。另外,对于设计模式,我不会像之前《Entity Framework之领域驱动设计实践》、《Microsoft NLayerApp案例理论与实践》那样特地开个专题去逐个介绍,我会根据实际项目中的使用情况来做针对性分析。比如单件模式,应用范围很广,而且结构简单,大家都知道应该在什么场合应用单件模式,我也就不会再费时间去做重复介绍了。
问题提出
在CodeDom代码生成中使用Decorator模式实现类型创建,这个话题来自我之前的一篇文章(《在Visual Studio 2010中使用Modeling Project定制DSL以及自动化代码生成》)所讨论的内容,文章最后介绍了使用CodeDom来根据Modeling Project自动化产生代码的方式,于是,本文所讨论的问题就来自于这个CodeDom实现代码生成的过程。首先还是回顾一下之前那篇文章中,我们所设计好的一个非常简单的模型类图:
这个模型类图很简单,就包含两个聚合根(Aggregate Root):图书(Book)以及分类(Category)。Category与图书之间是聚合的1:N的关系,而Category本身跟自己也是聚合的1:N的关系,这表明Category本身还可以包含多个Category。现在,我们就需要创建一个工具,它能够根据这个模型类图,通过使用CodeDom技术来自动化产生类的代码。要实现这个工具,就会牵涉到Modeling Project的读入、Modeling Project的Profile、Stereotypes以及CodeDom的具体技术细节,本文不对这些内容做太多介绍,朋友们还是到我之前写的这篇文章去了解这部分内容。
在使用CodeDom生成代码的时候,首先是创建CodeCompileUnit实例,然后创建CodeNamespace实例,并在CodeNamespace中添加CodeTypeDeclaration实例以向CodeNamespace创建类型定义,最后,VBCodeProvider、CSharpCodeProvider或者JScriptCodeProvider就可以根据CodeCompileUnit实例来实现代码生成。假设我们现在需要生成Modeling Project中的某个类,我们可能会使用下面的方式:
private CodeTypeDeclaration GenerateRegularClass(IClass clazz) { CodeTypeDeclaration codeTypeDeclaration = new CodeTypeDeclaration(); // 1. 创建类的声明部分 // 2. 处理属性的生成部分 // 3. 处理关联的生成部分 // 4. 处理接口实现部分 // 5. 处理类继承部分 return codeTypeDeclaration; }
而对于Book这样的聚合根,它又有自己的特性,比如它需要继承于AggregateRoot类,并在其中实现相应的方法,于是,我们就:
private CodeTypeDeclaration GenerateAggregateRootClass(IClass clazz) { CodeTypeDeclaration codeTypeDeclaration = this.GenerateRegularClass(clazz); // 在此处理聚合根的特性 return codeTypeDeclaration; }
事实上Modeling Project不仅仅包含了聚合根这种stereotype,还包含了诸如实体(Entity)、领域事件(DomainEvent)等stereotypes,要针对应用了这些stereotype的类创建类型定义,我们就要写一些类似GenerateEntityClass、GenerateDomainEventClass这样的方法。更进一步,如果某个类型需要在聚合根的类的基础上,再增加一些特殊的代码成分,那么在生成代码的时候,就需要首先调用GenerateAggregateRootClass方法获得聚合根的类型定义,再针对这些特殊的成分做进一步处理。如此一层套一层,不仅使得代码逻辑变得非常复杂,而且在对某种stereotype做进一步扩展或自定义的时候,会很不灵活。
分析问题
从上面的问题我们可以看到,在针对某个类创建类型定义的时候,首先通过new关键字创建了CodeTypeDeclaration的实例,然后就一步步地向CodeTypeDeclaration添加所需的代码成分,比如类的声明、属性、继承等等,进而对于应用了stereotype的类,再添加特定的代码生成成分。于是从CodeTypeDeclaration的角度看,它经历了类似下面的过程:
由此可见,CodeTypeDeclaration实例中的内容,是逐步“润色”上去的,我们可以套用Decorator模式来完成CodeTypeDeclaration的创建。
模式应用
以下是实现Modeling Project中类型代码自动生成相关的类图,为了节省篇幅,使读者看得更清晰,图中省略了几个Decorator的具体实现,但不影响对整个结构的理解。
相关代码如下:
interface IClassTypeGenerator { void Generate(IClass clazz, CodeTypeDeclaration codeTypeDeclaration); } abstract class ClassTypeGenerator : IClassTypeGenerator { public abstract void Generate(IClass clazz, CodeTypeDeclaration codeTypeDeclaration); } class ClassTypeDeclarationGenerator : IClassTypeGenerator { public ClassTypeDeclarationGenerator() { } public void Generate(IClass clazz, CodeTypeDeclaration codeTypeDeclaration) { // generate class declaration } } class ClassTypePropertyGenerator : ClassTypeGenerator { private readonly IClassTypeGenerator generator; internal ClassTypePropertyGenerator(IClassTypeGenerator generator) { this.generator = generator; } public override void Generate(IClass clazz, CodeTypeDeclaration codeTypeDeclaration) { this.generator.Generate(clazz, codeTypeDeclaration); // generate properties } } class ClassTypeApworksAggregateRootGenerator : ClassTypeGenerator { private readonly IClassTypeGenerator generator; internal ClassTypeApworksAggregateRootGenerator(IClassTypeGenerator generator) { this.generator = generator; } public override void Generate(IClass clazz, CodeTypeDeclaration codeTypeDeclaration) { this.generator.Generate(clazz, codeTypeDeclaration); // generate AggregateRoot facilities } }
那么在创建AggregateRoot的类型定义时,就可以:
CodeTypeDeclaration codeTypeDeclaration = new CodeTypeDeclaration(); new ClassTypeApworksAggregateRootGenerator( new ClassTypePropertyGenerator( new ClassTypeDeclarationGenerator())).Generate(clazz, codeTypeDeclaration);
与Builder模式的比较
设计模式将Builder模式归类到创建型模式,而将Decorator模式归类到结构型模式。而在我们的例子中,我们是使用了Decorator模式来创建(严格地说应该是逐步填充)了CodeTypeDeclaration对象。Builder模式用来创建具有特定结构的对象,而对象结构的组成部分可以有不同的实现方式;而Decorator模式则更擅长于向已创建的对象上填充内容。