标题:没头没尾--项目开发笔记:工具能生成多少代码!?
关键词:分布式开发 C# 项目分工 DELPHI与C#的混合开发 代码生成工具 数据库扫描
12月2号:开始写代码生成工具了,真有点千头万绪不知从何说起……
代码工具包括代码的生成工具以及代码同步工具两种类型的工具。当然,可以将代码的生成与同步写在同一个工程项目中。如果有充分时间进行考虑的话,那么我们的代码工具也的确是应该包含在一个工程中。但事实上由于时间与经验的关系,我没有做到把这两种东东放在一个工程中,而是分别的做出几个不同的代码生成工具与代码同步工具。为了说明的方便,下面的讨论都只是针对代码生成工具。(代码同步在一开始我考虑的比较少,所以后来出现了一些小问题,这个在下面会有描述。)
还有一点要强调的是,下面有很多的想法是在项目开发过程之中,或者已经开发完成之后才想出来的,所以有很多是只有想法,而没有具体的实现J。
开始之前一定要介绍一本书,书名:《.NET 企业应用高级编程》(清华大学出版社),这本书中讲述了一个代码生成工具的简单例子。这个简单的例子核心的东东包括两块:一块是对数据库进行扫描,将数据库的结构取出放入程序中的一个树形的结构;另一块是代码的生成与同步。不过这本书的代码生成与同步是运用Vs.net本身包含的Code DOM。所以最后生成出来的代码是只能在VS.net的环境中进行使用(只能生成C#或VB.net的代码文件)。而本项目的要求是要使用代码生成工具生成很多种类型的文件,于是我们只是参考了这本书的思路,如果有朋友们是完成使用Vs.net开发解决方案的话,是可以完全的参考此书。
代码生成工具这种东东在我们的项目之中应用的非常广泛。举几个例子来说吧
1. 对应数据库表以及存储过程的数据代码的生成;
2. 对应数据库表以及存储过程调用的从WebService到DataAccess所有文件;
3. 对应生成所有数据库表的服务器缓存文件代码,以及缓存机制中的代码;
4. 通过服务器端C#生成的XSD文件自动生成客户端Delphi可以用的ClientDataSet代码(项目组中Delphi的牛人制作);
5. 扫描数据库生成数据库表的基础存储过程;
6. ………………
由于我们做出的代码生成工具本身都是一些比较零散的小工具,所以我们都没有对他们进行总结,呵呵,每次我想去总结的时候总是给自己找到一个借口:总不能把代码生成工具做的比程序还有复杂吧,那要代码生成工具干嘛?所以上面写的那些类都还是单独的零零碎碎的工具。真是有点无从说起的感觉。
下面主要从代码生成工具的简述,代码生成工具开发后的一些感想,以及代码生成工具带来的开发过程的一些变化三个方面来描述。
代码生成工具简述
这样吧,我先从代码输入、生成、输出三个方面讲讲我理解的写代码生成工具的原则(如果这些可以叫原则的话)。
l 输入
如何找出输入是代码生成工具的核心。我觉得这个部分是很能体现出开发人员的想像力。(有点自卖自夸。)我觉得理论上如果你可以把你的需求完成与直接的变成代码生成工具的输入,那么其它的开发人员就可以什么都不用干了J。你可以吗?目前当然是不行。但是我们可以尽可能的找出多种方式的输入,多种类型的输入,以使我们的代码生成工具生成更加靠近最后实现程序的代码。
由于我们的层次分的很多,那么也就存在如何组织每一层代码的问题。我不清楚其它朋友们对分层次的框架结构中代码的组织定义出什么样的规则。我对这事是简单的定义出三种类型的代码组织方式;DO层可以按照数据库表与数据库存储过程对应的结构来组织代码;在BusinessRules层可以按业务设计中的对象来组织;而facade,webservice与UI层当然是要按面向用户的模块来进行组织。针对上面三种代码组织方式,都可以通过代码生成工具至少生成基础的文件,以及文件的组织形式。但是各种情况的输入方式是不一样的,下面分别进行说明。
n 面向用户的界面
界面的代码组织情况是需要按照交互设计的具体情况来进行组织。比如系统中可以按照总公司模块,分公司模块等等方式来进行界面代码文件的组织。在这种情况下,如果要生成面向用户界面的代码框架,除了首先要统一的制作出各个界面的模板之外,还必须要有一个面向用户界面的代码组织形式的描述。我们在这里本来是希望使用一个excel文件描述这个组织形式,然后通过VBA将内容读出,生成代码,不过后来由于时间的关系,并没有真正这样进行:(;这个原理应该是与ROSE中生成代码的原理基本相同的。
对应生成的是本系统中的UI,Web Service层的代码。
n 面向对象的业务
面向对象的业务组织形式不用多说的。本身我们的系统设计上做的粒度非常的粗,所以从对象上我们也只是分成了一些大的对象,比如机构,商品,单据。没有具体的进行细分。也就存在着按这种分类方式来组织我们的代码。同样由于时间的关系,这一部分我们暂时也没有
对应生成的是本系统中的Facade层与Business Rules层的代码。
n 面向关系的数据库
写到这里真的是有一点惭愧,上面基本上都是光说不练的。有的部分我也只是写了一些从excel文件中读取一些数据的类而已,这些也就不多说了。也许会在项目的中后期去实现的,让以后的项目笔记中去记录吧。
数据库关联的代码生成是我们已经完成了,我做的工具是根据负责数据库层开发的同仁已经对应数据库的表结构生成了Common Data与DataAccess.Base中的代码之后(这两层的代码是通过拖拽方式来生成。生成之初是与数据库中的表进行了一次组织,对简单的代码表,表与操作类是一一对应的关系,对于主子表,是放在一个操作类来进行处理的。)我根据他的代码以及代码的结构生成对应的DataAccess.Template与DataAccess层的代码。其中生成的DataAccess.Template中的代码类是继承DataAccess.Base中对应父类的一个子类。主要的作用是针对不同种操作类生成的一些固定的方法(对简单表的SelectAll, Insert, Updata, Delete方法,单据表的单号生成等等),同时生成了继承DataAccess.Template的DataAccess层的所有代码。
生成的过程中需求对数据库进行扫描来得到对应的Key column,各个Column的名字,可以采用SQL的系统视图来得到数据库的结构。比如:information_schema.tables、information_schema.columns等等,可以参考上面提到的书。
实际上,在做上面的过程之前,我还并没有意识代码的组织形式可以是面向用户,面向对象,关系数据库等三种组织形式。我只是最简单的认为只要提供出数据库表以及存储过程对应可以调用的方式,就可以在这个基础上完成所有的界面代码编写所需要的支持(如果对我定义的开发过程不了解可以参考以前的项目笔记)。这一部分工作实际上是先于开发编码过程的。按照我对开发过程的规划,我希望是在编码的第二步中让开发人员使用生成的代码来完成界面层与数据库的连接。想通过生成的代码实现对界面编程的服务器端的支持。于是我对应生成不只是Do层的代码,而且将Business Rules层、Facade层、Web Service层的代码按关系数据库组织形式进行了一次组织。
对应生成的是本系统中的Data Access层与Data Access. Template层的代码。
上面说的这三类我想主要描述的是,开发过程的开始阶段,项目中已经设计或已经实现的部分是如何一一对应到编码的过程中。比如面向用户的界面交互设计完成之后,会按什么样的方式组织客户端的编码过程;又比如数据库结构已经基本定下来了以后,如何去快速的生成服务器端的可视的数据库的编码(虽然这个编码在后面给我们带来麻烦,不过项目一开始的时候对开发人员理解项目还是有很多好处的。)
还有就是,我对代码组织形式的分法也希望与朋友们进行讨论。不知道朋友们对自已定义出的工程是如何组织管理的?!
l 生成方法
代码的生成方法我们主要有三种:
n 字符写入法
这种方法是我在一开始做的代码生成工具的时候采用的方式。可以说是一种比较笨的方法。这种方法就是把大量的要输出的文件中固定的字符当成字符串写在程序中。把上面提到的输入夹在中间,形成个性化的代码。比如以下代码是生成DataAccess.Template文件的一段代码:
String szTemp = String.Empty;
szTemp += “using System;\n\n”;
szTemp += “namespace XXX.Develop.FrameworkBuilder.ProjectBuilder\n”;
szTemp += “{\n”;
szTemp += “ public class ” + szTableName + “Template\n ”
//生成的DataAccess.Template类统一以CommonData的名称加上Template作为结尾
………………
可以看出,使用这种方式生成代码的话,最有可能出现的问题就是如果你的生成规则有变化,那么你要重新去修改你的代码生成工具的源程序。这样非常的麻烦……
n 字符代替法
这是我目前使用的方法。这个方法是将相对于输入是有规则的代码文件生成模板文件。这样模板文件生成之后,代码生成工具将会在执行的过程中使用输入来替换模板中对应的特殊符号块。下面同样以生成上面的代码为例:
首先是生成代码模板文件:
using System;
namespace XXX.Develop.FrameworkBuilder.ProjectBuilder
{
public class //?? CommonData ??//Template
{………………
然后针对每一个CommonData中有的类,取出类名,替换上面的特殊符号块//?? CommonData ??//,这样同样可以生成与字符写入法相同的代码。
同样可以使用这种方式来修改Vs.net中的工程文件。比如下面是对应StaticData层的工程文件StaticData.csproj中的模板中的一段代码:
<Files>
<Include>
<!-- StaticDataList -->
<!-- //??
<File
RelPath = "//?? CommonData ??//StaticData.cs"
SubType = "Code"
BuildAction = "Compile"
/>
//?? -->
<!-- EndStaticDataList -->
</Include>
</Files>
………………
(注:其中“<!-- StaticDataList -->”是循环生成的开头特殊符号,“<!-- EndStaticDataList -->”是循环生成的结束特殊符号,“<!-- //??”是循环中每一项要生成的开始特殊符号,“//?? -->”是循环中每一项要生成的结束特殊符号。中间的<File之类的代码将会对应生成一个StaticData的文件就加入一次。)
这样的话可以在重新生成代码时同时更新工程文件(不过这样可能会与SourceSafe配合使用时需要注意Check的问题)
n Code DOM
Code DOM是VS.net中提供的代码生成工具,上面推荐的书中使用了这个进行代码生成。Code DOM对C#与VB.net的代码生成支持的很好,不过似乎没有看见可以使用Code DOM来对工程文件进行修改,而且也不支持生成Delphi的代码。所以本项目没有采用这种方式。
l 输出
输出的应该是写代码的人员可以“利用”的代码。“利用”的意思是就是应该是可以在生成的代码的基础上进行不同层次的跳跃开发,以及可以对生成的代码进行修改。对于输出的我想说的只有,如果你在开始去制作一个代码生成工具的时候,最好在设计的时候就把代码的生成与代码的同步,维护放在一起进行考虑。(本项目实际上并没有做到这一点,所以后来对生成的代码的维护比较头痛。)
一些感想
制作代码生成工具的时候,我一直有一些想法,虽然这个想法现在还很不成熟,但是我还是想先说出来与朋友们讨论一下。
一直以来,我所呆过的几家公司都有这样的问题,那就是如何共享开发人员的经验,项目得来的经验。我记得在以前的项目笔记中我提到过,如何去把项目的经验留下来?如何使得项目的经验成为公司的财产,而不是个人的财产?对于这个问题,我以前的想法非常的简单,就是项目文档化,加强项目文档痕迹管理。可是这样又会直接的出现一个问题,就算我完成一个项目留下10000篇的项目文档,可是当下一个项目来临的时候,谁又有时间去仔细的体会你的10000篇文档?我也和一个以前是国企的朋友聊过项目经验的保存问题。他告诉我,从他的经验看项目经验在国企的保存与传递的方法就是“带徒弟”。老师付通过带徒弟把经验一代一代的传下去。可是在新技术发展的今天,项目经验的保持还只是靠这种方式来做的话,就已经跟不上时代了。
我的想法是,代码生成这种形式其实可以很好的应用到保存项目经验中。上面已经写了,我们的工程生成的代码与具体的开发语言是没有什么关系的。并且一但定义好了那些用来替换的特殊符号,以及真正去想清楚你的输入可能会有什么情况。一个富于经验的人来写出模板文件,此模板文件,或者说此模板工程(模板文件的组合)将会作为经验的延继。新的项目中可以通过修改以前项目的模板文件来生成新模目的所有支持文件,开发人员可以将精力更多的集中到业务中。如果这种方法可以的话,那么会不会还有类似这样的辅助过程来修改开发过程,实现对项目小组中全部人员的提高?(这还只是一个想法,希望与朋友们讨论)
对开发过程的改造
使用代码生成工具的好处我想在这里我也不多说了,不外乎提高开发效率之类的说法。这里我想强调的一点是在我们使用代码生成工具之后(实际上我最多的情况还是与数据库的开发人员一起来用代码生成工具)。我们对于代码的规范性,命名的规范性其实提高了一个新的层次。以前你的文件名,数据库表,存储过程名称都可以在规则许可的条件下自由的命名,但是这一次的文件命名,以及其中数据库建立的表结构将会直接与你是否好使用代码生成工具生成代码有关。也就是实际上进一步的加强了对数据库,编码规则的控制(要不能用生成工具生成代码的话,数据库负责的哥们就要自已手工写很多的程序。)
例如:由于我们对主表与明细表的关联是放在一个CommonData中,那么我们定义出必须将明细单的名字取成“主表名+Detail”,这样的话,生成代码时可以少判断一些条件。类似这样的规则的强化还有。
代码生成工具的三个部分就先写到这里,如果下面有时间的话,我会具体的写一写与数据库联系部分的代码生成工具的具体过程。
(哈,项目中的负责数据库的同仁也开始做出一个小的生成工具来生成比较统一的存储过程代码,加一条记录,删除记录,还有分段取数据,呵呵,项目中的同仁这么干我觉得真的很开心。)
(这几天项目中已经陆陆续续的出现我们使用分层开发方式的一些问题与优势。不过还没有来的及进行总结。有时间的话再写出来吧。)