代码生成器的设计理念和实践
李俊杰
概论
代码生成器十分有利于提高编码效率和项目进度,并且有利于代码的规范化管理,把程序员从繁琐的重复性的代码编写中解放出来,特别是同时产生代码和配置文件(config),这样避免了多个文件之间的互相引用带来的错误(当你查找bug精疲力竭,最后发现仅仅是由于你的配置文件中的配置时某一个参数的字母大小写的错误,你什么感受),所以颇受的广大程序员和项目管理者的青睐。
设计思想分类
代码生成器的设计思路其实很简单。简而言之,包括输入模块,加工模块,输出模块。
输入模块
所谓输入模块主要是组织“原料”,该原料可以是数据库表(如hibernate的代码生成器),也可以是文本文件(最常用的是xml文件,或者java代码文件如doclet代码生成器)或者别的途径。
加工模块
加工模块是根据输入模块产生的原料按照一定的规范(模版)生成字符串(广义的字符串,即包括要生成文件的内容 )。
输出模块
输出模块的功能是把代码生成器生成的代码字符串写入一系列指定目录和文件名称文件。
设计分类
1) 无模版设计类型。事实上该模版在设计者的心中,也就是说设计者非常清楚生成的文件的格式和内容。后面的篇幅中会简单介绍该设计思想,其优点是很灵活,缺点是扩展性和适应性较差。如同样的“原料“,生成新格式的文件就会很被动,需要重新编程。
2) 模版设计类型。这是现在最广泛的的运用设计思路。就是加工模块首先读模版文件,根据模版文件中的标志,把相应的“原料“填充到替换标志。然后生成新的文档。
实现原理简介
1)无模版设计类型示例:
//设置文件中的格式
//回车
public static final String RETURN_ROW = "\n";
//2个空格
public static final String BLANK2 = " ";
//4个空格
public static final String BLANK4 = BLANK2 + " ";
//6个空格
public static final String BLANK6 = BLANK4 + " ";
public static final String BLANK8 = BLANK6 + " ";
public static final String BLANK10 = BLANK8 + " ";
public static final String BLANK12 = BLANK10 + " ";
public static final String BLANK14 = BLANK12 + " ";
public static final String BLANK16 = BLANK14 + " ";
public static final String BLANK18 = BLANK16 + " ";
public static final String BLANK20 = BLANK18 + " ";
public static final String BLANK22 = BLANK20 + " ";
public static final String BLANK24 = BLANK22 + " ";
下面是读。Xml文件,根据读进来的原料,进行加工。
String serviceID = serviceConfig.getServiceID();
String serviceName = serviceConfig.getServiceName();
String serviceDesc = serviceConfig.getServiceDesc();
String serviceTypeName = serviceConfig.getServiceTypeName();
String packageName = serviceConfig.getPackageName();
String pureServiceName = serviceName.substring(0,serviceName.lastIndexOf("Service"));
String serviceClassName = packageName + "." + serviceName;
String inServiceBeanClassName = packageName + "." + pureServiceName + "InServiceBean";
String outServiceBeanClassName = packageName + "." + pureServiceName + "OutServiceBean";
String serviceUnitTestClassName = packageName + "." + pureServiceName + "ServiceUnitTest";
String canTest = serviceConfig.getCanTest();
String noSecurityCheck = serviceConfig.getNoSecurityCheck();
String dbServerName = serviceConfig.getDbServerName();
String serviceNo = serviceConfig.getServiceNo();
//下面是生成新的字符串
StringBuffer sb = new StringBuffer();
sb.append(beginTagRow(ProxyXMLCodeGenerator.BLANK2,"service"));
sb.append(generateRow(ProxyXMLCodeGenerator.BLANK4,"serviceNo",serviceNo));
sb.append(generateRow(ProxyXMLCodeGenerator.BLANK4,"serviceID",serviceID));
sb.append(generateRow(ProxyXMLCodeGenerator.BLANK4,"serviceName",serviceName));
sb.append(generateRow(ProxyXMLCodeGenerator.BLANK4,"serviceDesc",serviceDesc));
sb.append(generateRow(ProxyXMLCodeGenerator.BLANK4,"serviceTypeName",serviceTypeName));
sb.append(generateRow(ProxyXMLCodeGenerator.BLANK4,"packageName",packageName));
sb.append(generateRow(ProxyXMLCodeGenerator.BLANK4,"serviceClassName",serviceClassName));
sb.append(generateRow(ProxyXMLCodeGenerator.BLANK4,"inServiceBeanClassName",inServiceBeanClassName));
sb.append(generateRow(ProxyXMLCodeGenerator.BLANK4,"outServiceBeanClassName",outServiceBeanClassName));
sb.append(generateRow(ProxyXMLCodeGenerator.BLANK4,"serviceUnitTestClassName",serviceUnitTestClassName));
sb.append(generateRow(ProxyXMLCodeGenerator.BLANK4,"canTest",canTest));
sb.append(ProxyXMLCodeGenerator.BLANK4).append("<noSecurityCheck>1</noSecurityCheck>").append(ProxyXMLCodeGenerator.RETURN_ROW);
sb.append(generateRow(ProxyXMLCodeGenerator.BLANK4,"dbServerName",dbServerName));
sb.append(ProxyXMLCodeGenerator.BLANK4).append("<callServices/>").append(ProxyXMLCodeGenerator.RETURN_ROW);
System.out.println(sb.toString());
这样就生成了某一定格式的xml的字符串。
2)模版设计类型示例
小试牛刀,最基础的替换原理
public static String transit(HashMap map, String template){
if(template == null){
return null;
}
String ret;
StringBuffer sb = new StringBuffer();
int beginIndex = 0;
int endIndex = -1;
int t = 0 ;
int i = 0;
String key = null;
while((t = template.indexOf("$",beginIndex)) != -1 ){
endIndex = t;
if(beginIndex + 1 == endIndex){
beginIndex = endIndex + 1;
continue;
}
key = template.substring(beginIndex, endIndex);
i++;
if(i % 2 == 1){
sb.append(key);
}else{
sb.append((String)map.get(key));
}
beginIndex = endIndex + 1;
}
if((t = template.lastIndexOf("$"))!= -1){
sb.append(template.substring(t+1));
}else{
sb.append(template);
}
ret = sb.toString();
return ret;
}
public static void main(String[] args){
HashMap map = new HashMap();
map.put("name","lisi");
map.put("age","25");
map.put("address","beijing");
map.put("year"," years old");
map.put("desrc","Read me : ");
String[] names = new String[]{"name", "age","address","year","desrc"};
String[] values = new String[]{"lisi", "25", "beijing"," years old","Read me : "};
String template = "$desrc$My name is $name$, my old is $age$$year$, I live in $address$.";
System.out.println(StringUtils.transit(map, template));
}
下面是工业化版本
//seq语句模版
#loopBegin#
INSERT INTO "TX"(
"TXID",
"DEPT_CLASS",
"TXNAME",
"SEP_GRANT_FLAG",
"CROSS_OP_FLAG",
"MGRBRANCH_OP_FLAG",
"SECURITY_CHK_FLAG",
"SECURITY_TX_FLAG",
"VALCONSTR_FLAG",
"TX_TYPE",
"CI_GROUP_NO"
)
VALUES(
'$serviceId$',
'$deptClass$',
'$serviceDesc$',
'1',
'1',
'1',
'1',
'1',
'0',
'U',
'1'
)
/
#loopEnd#
首先我要解释一下该模版中的标志#loopBegin#和#loopEnd#表示这中间的代码要进行循环,其中“$$,##”为识别符,因为在模版中要识别该代码段要进行特殊的操作或者替换(另外识别符的选择一般是比较生僻的字符,在原始的代码中难以看到,如果原始的代码中真的存在该字符,可以用转字符来替代,如“\$,\#”),$serviceId$表示把“原料”中的serviceId的值替换到该处,生成新的代码字符串。
实现代码(下面的代码是我从相关的类中摘取的代码),我们的模版是以文件存在的,而文件是由一行行字符串组成的,因此我把处理的最小单元设计为一行。
/**
*
* @author lijunjie
* @param
* @return String
* @exception
* 方法描述:把从模板文件中读取的某一行进行转换,把其中的参数替换成实际的代码
*/
public String transit(ArrayList lines,HashMap map){
StringBuffer sb = new StringBuffer();
String line = (String)lines.get(0);
StringTokenizer tokenizer = new StringTokenizer(line,"$");
int i = 0;
String name;
while(tokenizer.hasMoreTokens()){
i++;
if(i % 2 == 1){
sb.append(tokenizer.nextToken());
}else{
if((name = (String)map.get(tokenizer.nextToken())) != null){
sb.append(name);
}
}
}
return sb.toString();
}
/**
*
* @author lijunjie
* @param
* @return String
* @exception
* 方法描述:依据模板生成文件内容的字符串,
* 根据模板文件的内容来进行不同的处理,使用策略模式来创建不同的处理类.
*/
public String generateCode(String templateFile) {
分享到:
相关推荐
编辑原理的中间代码生成器设计C语言版
自己写的小工具代码生成器瞬间生成代码自己写的小工具代码生成器瞬间生成代码自己写的小工具代码生成器瞬间生成代码自己写的小工具代码生成器瞬间生成代码
*关键 C# 代码生成工具 C# 代码生成工具CodeMissile.Net 代码生成工具
.net代码生成器 C#代码生成器内置辅助类!!!一键生成含所有源码!!!
适用于java/C/C++等代码,源代码为java代码通过运行代码将代码输入程序框中D盘自动生成伪代码文件PseudoCode.txt。(注意:由于是简单代码只可适用于糊弄老师,实验报告等,不可用于学术研究)。
四个代码生成器,主要是对数据库程序员的自动代码生成器
动态代码生成器动态代码生成器动态代码生成器动态代码生成器动态代码生成器动态代码生成器动态代码生成器
代码生成器是整个程序最后生成最终代码的部分模块。它将前面所分析得到的所有信息进行汇总,将变量表中的数据再写回程序代码段中。 此部分分为两个功能模块: 1.中间代码生成。将所有段落的代码按照格式和顺序...
c#代码生成器,用于生成三层结构体系代码
C# 数据库代码自动生成工具,方便实用,快速开发好帮手!
动软代码生成器,自动生成设计模式代码, 动软代码生成器,自动生成设计模式代码 动软代码生成器,自动生成设计模式代码 动软代码生成器,自动生成设计模式代码
MFC代码生成工具MFC代码生成工具MFC代码生成工具
SSH代码生成器
Hibernate代码生成工具 设计全攻略
代码生成器代码生成器代码生成器代码生成器代码生成器
C#代码自动生成工具,自动生成三层架构代码
存储过程代码生成器存储过程代码生成器存储过程代码生成器存储过程代码生成器存储过程代码生成器存储过程代码生成器
动软代码生成器是一款完全自主知识产权研发的为软件项目开发设计的自动代码生成器,也是一个软件项目智能开发平台,它可以生成基于面向对象的思想和三层架构设计的代码,结合了软件开发中经典的思想和设计模式,融入...
编译原理中间代码生成器实现C++编译原理中间代码生成器实现C++