张文江,何政伟,李旭龙
(1.成都理工大学地质灾害防治与地质环境保护国家重点实验室,四川成都610059;2.成都理工大学地球物理学院,四川成都610059)
数据采集实现屏幕显示元素数据到数据对象属性的转换,而数据显示则实现数据对象属性到屏幕显示元素数据的转换,二者统称界面元素与数据对象之间的数据交换。界面元素与数据对象之间的数据交换是各类系统的普遍功能,当前有多种技术模式实现这些功能,比如“拷贝—粘贴—修改”模式、基于框架的模式、基于委托与事件方法的技术等。对于数据项少、界面简单的系统,可以采用“拷贝—粘贴—修改”的编程模式;典型的框架,如Struts、Spring,都提供了表现层 (界面)和业务层 (数据对象)之间的自动数据交换功能,并被广泛用于Web系统的开发[1-3];也有采用基于委托与事件方法的数据交换模式系统[4],但这类系统比较少。上述这些方法都具有比较大的局限性:在人机交互界面复杂、屏幕显示的数据项很多的情况下,“拷贝—粘贴—修改”模式产生的代码冗长、相似度高,造成程序调试疲劳,隐含错误难于发现,调试、维护工作量大;而struts、spring是基于web系统的,不支持winform,另外,对单选、多选等复杂数据项,struts、spring的支持较弱;基于委托与事件的方法太过复杂。
基于反射技术实现数据交换的公共函数,各个窗体界面通过对公共函数的调用,完成winfrom窗体或web页面的显示界面元素与数据对象之间的数据交换。这种方法能够极大减少复杂界面在数据显示、数据修改方面的程序代码,减少程序调试工作量,提高工作效率,提高系统的可靠性。
反射是本文提出的数据交换技术的基础,下面先从反射的概念开始。
反射就是程序在运行时刻能够查询类型的信息,包括类型状态 (属性)、类型行为 (方法)、事件、构造函数等信息。它本质上是分析程序集中的元数据表的过程,发现程序集的类型和类型成员的信息[4]。它是一种晚绑定机制,程序员在写代码的时候还不知道或不能确定的一些信息,由代码在运行的时候确定。当前软件开发的两大体系,Java和.Net都具有强大的Reflection机制,并且得到了广泛的应用[5-10]。
下面以一个简单的例子,说明反射的基本思想。设系统中有一个StudentClass类,其定义如图1所示。
图1 学生类
对象obj是程序运行过程中自动生成的,其类型在编译的时候未知,所以程序代码不知道obj的属性、方法,也就不能直接访问obj的属性和方法。但是程序在运行时通过反射可以获得obj的类型、属性、方法等信息,通过属性和方法的名称实现对obj的管理。如果obj的类型为Student-Class,就可以通过 StudentClass类的属性名称“No”、“Name”读写obj的No属性值,通过“GetAge”方法访问obj的GetAge函数,获得学生的年龄信息。
由于本文实现的系统是基于C#的B/S系统,因此,本文提供的算法将以基于C#语言的Asp.Net为例,实现Web页面可视控件与C#数据对象之间的数据交换。数据库中的数据与可视控件之间的数据交换问题,可用类似的方式实现,或者通过建立数据库记录与C#数据对象之间的映射关系,实现数据库记录与C#数据对象、C#数据对象与可视控件之间的双层数据交换。
ASP.Net页面对象,不管是标准的HTML对象,还是Asp.net封装对象,只要加上Runat=Server,C#代码就能访问该控件的属性,在系统运行的过程中动态获取页面控件类型、对象嵌套关系等信息。实际上,C#能够全面操控整个ASP.Net文档:遍历整个DOM文档,并对DOM文档的任意对象进行控制。
另一方面,对于C#数据对象,采用反射技术,按封装的原则,通过属性的getter、setter方法,实现属性字段值的读、写操作。
由此可见,ASP.Net页面特点和C#反射技术,为页面元素与数据对象属性之间的数据交换提供了可能。关键是如何在页面元素与数据对象之间建立起通信的桥梁。
本文使用“名称关联法”建立页面控件与数据对象属性之间的关联,使Web页面可视控件的ID与数据对象属性名称相同 (或者其它类似的规则),从而建立起它们之间的对应关系。程序在遍历Web页面可视控件的时候,根据可视控件的ID,找到数据对象的属性名称,再通过C#提供的反射机制,查询数据对象该属性的相关信息,读取、或设置该属性的值,从而实现可视控件显示值与数据对象属性值之间的相互数据交换。
图2是这种基于名称对应关系的简化图示说明,其中界面元素ID=Property1对应类ObjectClass的属性Property1,界面元素ID=Property2对应类ObjectClass的属性Property2,界面元素 ID=Property3对应类ObjectClass的属性Property3。
图2 基于名称对应的显示元素与数据对象关系
在阐述原理后,下面首先详细分析各种显示控件的处理方法,然后给出实现算法。
(1)简单控件
简单控件是指直接显示数据属性值的控件,这类控件使用最广,如TextBox、HtmlInputText、Label等,这种显示控件与数据对象之间的数据交换简单、直接。
显示控件命名规则:ID名称就是数据对象的属性名称。
如果属性数据类型为字符串型,可以通过设置Max-Length属性来限制显示长度。如果未设置MaxLength,或MaxLength设置为0,表示长度不受限制。
如果属性数据类型为小数,可以通过设置精度属性Precision来设置小数位数。
显示对象事例:
<asp:TextBox ID="V1"runat="server"Max-Length="30" ></asp:TextBox>
<asp:TextBox ID="V2"runat="server"Precision="5.2"></asp:TextBox>
<asp:TextBox ID="V3"runat="server" ></asp:TextBox>
其中显示对象ID名称对应数据对象属性名称。
(2)单选控件
单选控件也是常用的显示控件,在屏幕上表现为一组单选按钮,对应一个数据对象属性。与简单控件不一样的是,简单控件与数据对象属性是1对1的关系,而单选一般是多对1的关系:多个单选框对应于一个数据对象属性。
为了简化编程代码,本方案用一个DIV容器包含这一组单选按钮,由DIV容器代表这一组单选框。
该DIV命名规则:DIV的ID名称设置为数据对象属性名称,同时,为了标识该DIV包含的是单选按钮,该DIV增加一个固定属性:groupClass="radiogroup"。
C#遍历Web页面时,一旦发现属性groupClass=radiogroup,就用DIV的ID作为数据对象属性名称,然后通过反射查找数据对象相应属性的值val,再遍历DIV的radio子对象集,如果找到某radio的值与val相同,设置该单选按钮的checked状态为true。
显示对象事例:
<div id="hazardType"runat="server"group-Class="radioGroup" >
<asp:RadioButton ID="sel1"Text="滑坡" runat="server"/>
<asp:RadioButton ID="sel2"Text="崩塌"runat="server"/>
<asp:RadioButton ID="sel3"Text="泥石流"runat="server"/>
</div>
要点:
1)Div对应数据对象属性,单选按钮作为div的子对象;
2)div的id名称与数据对象的属性名称1:1对应;
3)groupClass="radioGroup"是固定属性;
4)单选按钮的ID命名没有限制
(3)多选控件
与单选类似,一组多选按钮对应一个数据对象属性,因此,在进行Web页面设计时,也要用一个DIV容器把多个多选按钮组织在一起,并且该DIV的ID名称为数据对象属性的名称,该DIV也要增加一个固定变属性:groupClass="checkgroup"。
C#遍历Web页面时,一旦发现属性groupClass=checkgroup,就用DIV的ID来查找数据对象的相应属性值val,再遍历DIV的checkbotton子对象,如果该按钮的值在val中,则设置该按钮的Checked属性为true,否则为false。
显示对象事例:
<div id="scope"runat="server"groupClass="checkGroup" >
<asp:CheckBox ID="居民点" runat="server"/>
<asp:CheckBox ID="工厂"runat="server"/>
<asp:CheckBox ID="农田"runat="server"/>
<asp:CheckBox ID="水利工程"runat="server"/>
</div>
要点:
1)Div对应数据对象属性,多选按钮作为div的子对象;
2)div的id名称一定要与数据对象的属性名称对应;
3)groupClass="checkGroup"是固定属性;
4)单选按钮的ID命名没有限制
(4)下拉列表
下拉列表是一种填充了数据的列表,填充的数据类型是一种“代码—名称”数据结构,其特点是下拉列表显示名称,数据对象保存代码。
如果数据对象中保存的值就是显示名称,可以认为是“代码—名称”的一种特殊情况:即代码与名称相同。
下拉列表可以在设计时通过人工输入建立,也可以在程序运行时由代码动态生成。
下拉列表与数据对象属性具有1:1关系,下拉列表的ID名称与对应的数据对象属性名称相同。显示对象事例:
<asp:DropDownList ID="scale"runat="server" >
<asp:ListItem Value="01" >特大型</asp:ListItem>
<asp:ListItem Value="02" >大型</asp:ListItem>
<asp:ListItem Value="03" >中小型</asp:ListItem>
</asp:DropDownList>
要点:
(5)下拉列表的id名称与数据对象的属性名称对应;
在本系统中,单选结果以单选按钮显示的名称以字符串的形式保存在数据对象中,多选结果以多选按钮显示的名称保存,并以逗号 (,)分割多选值。为了算法叙述的方便,引入“规范子串”的概念,严格定义多选值的存储格式。
定义1:字符串 s是字符串 f的规范子串的充要条件是:
f以 s结尾或者 s加逗号 (,)形成的新串是 f的子串。
入口参数:
WebControl:页面控件,子控件命名规则符合上面提出的要求。
Obj:C#数据对象。
Function WebPageToEntity(WebControl,Obj)
{
通过反射,获得数据对象Obj的类型T;
获得WebControl的ID,并赋到变量pname;
如果ID不符号上面定义的规则
{
对WebControl的子对象,递归调用本函数
Return
}
取WebControl表示的数据类型,dataType;
如果dataType不是预定义的处理的数据类型,返回;
Case dataType是简单数据:
取WebControl的值,并赋到val;
通过反射,把val保存到Obj的pname属性;
Case dataType是单选数据:
查询WebControl的单选子控件,取选中的单选按钮的值,并赋到val;
通过反射,把val保存到Obj的pname属性;
Case dataType是多选数据:
查询WebControl的多选子控件,组合所有选中的多选按钮的值,并赋到val;
通过反射,把val保存到Obj的pname属性;
Case dataType是下拉列表数据:
取下拉列表选择的值,并赋到val;
通过反射,把val保存到Obj的pname属性;
Case其它:
不予处理;
}
入口参数:
WebControl:页面控件,子控件命名规则符合上面提出的要求。
Obj:C#数据对象。
Function EntityToWebPage(WebControl,Obj)
{
通过反射,获得数据对象Obj的类型T;
获得WebControl的ID,并赋到变量pname;
如果ID不符号上面定义的规则
{
对WebControl的子对象,递归调用本函数
Return
}
取WebControl表示的数据类型,dataType;
如果dataType不是预定义的处理的数据类型,返回;
Case dataType是简单数据:
通过反射,取Obj的pname属性值,并赋值到val
把val设置WebControl控件;Case dataType是单选数据:
通过反射,取Obj的pname属性值,并赋值到val
查询WebControl的单选子控件,如果某子控件的text属性值与val相等,设置该子控件的Checked状态为true;
Case dataType是多选数据:
通过反射,取Obj的pname属性值,并赋值到val
对WebControl的所有多选子控件执行:
如果该子控件的text属性是val的一个“规范子串”,
选中该子控件,否则不选中该子控件
Case dataType是下拉列表数据:
通过反射,取Obj的pname属性值,并赋值到val
把val赋值到下拉列表选择的值
Case其它:
不予处理;
}
贵州省地质灾害数据管理系统管理的灾害包括:滑坡、泥石流、崩塌、地裂缝、地面沉降、地面塌陷等6种,每种地质灾害的属性数量多达200个以上,并且属性类型复杂,单选属性、多选属性、下拉属性多,造成显示界面非常复杂。在项目原型阶段,采用“拷贝—粘贴—修改”方式,仅实现滑坡灾害数据对象到web页面的显示就写了1850多行代码,并且因为属性太多,代码相似度又极高,导致代码中存在很多难于发现的低级错误,调试极其困难,最后只能彻底放弃这种方法。
通过讨论,采用了基于反射的数据交换方法,只用了两个公共函数,就实现了所有地质灾害属性的显示与修改工作。函数WebPageToEntity实现web页面显示内容赋值到数据对象,代码只有160行;函数EntityToWebPage实现数据对象到页面的显示,代码只有158行。总共300多行代码,实现了所有Web页面与数据对象之间的数据交换功能。并且这300多行代码结构严谨、逻辑性强,调试、排错也很容易,系统的正确性得到了极大提高。
数据对象通过代码生成工具,直接从数据库生成。设计人员只把页面设计好,并按规范命名各种控件,最后通过调用WebPageToEntity和EntityToWebPage函数,实现了数据对象与显示元素之间的数据自动交换。数据对象的持久化通过iBatis实现。这样,CRUD部分的代码量和测试量就变得很小了,极大减少了系统开发的工作量。
文章提出了基于反射的显示控件与数据对象之间的数据交换原理和方法,并对文本框、单选框、多选框、下拉列表等常用显示控件的处理作了深入、详细的讨论,设计了实现数据交换的算法。
该方法具有普遍意义,适用于各种系统的CRUD操作,尤其对数据项多、界面复杂的系统能够极大地减少编程代码,减少程序错误,提高系统的正确性和稳定性。
该方法不局限于C#,可适用于支持反射机制的任何语言 (如Java),也不局限于Web系统,还可适用于winform桌面系统。
[1]GUO Guangjun,XU Zhangfa,YANG Siqing.Digitized archives management system for teaching evaluation based on struts-hibemate architecture[J].Computer Engineering and Design,2010,31(10):2358-2362(in Chinese).[郭广军,徐章法,羊四清.基于Struts-Hibernate构架的数字化教学评估档案管理系统[J].计算机工程与设计,2010,31(10):2358-2362.]
[2]ZHOU Xiangbing,YANGXiaoping,YANGXingjiang.Application and implementation of general framework based on web layering structure[J].Computer Engineering and Design,2008,29(7):772-775(in Chinese).[任晓鹏,赵文兵,张春平.基于框架的Web系统开发研究[J].计算机工程与设计,2010,29(7):772-775.]
[3]ZUO Wenming,WANG Qingqiang,WU Yingliang.Use land management information system based on struts[J].Computer Engineering and Design,2008,29(19):4972-4974(in Chinese).[左文明,王庆强,吴应良,等.基于Struts的用地管理信息系统[J],计算机工程与设计,2008,29(19):4972-4974.]
[4]CHEN Nannan.Research and implementation of data transmission with C# [J].Computer and Information,2011(11):67-68(in Chinese).[陈南南.基于C#的对象间数据传递方法分析及实现 [J].电脑与信息,2011(11):67-68.]
[5]ZHANG Zhi,DU Jingkang,ZUO Fengjun,Research on.Net reflection based GISplugin technology[J].Science of Surveying and Mapping,2011,36(4):151-152(in Chinese).[章志,都金康,卓凤军.基于.NET反射机制的GIS插件技术研究[J].测试科学,2011,36(4):151-152.]
[6]ZHOU Xiangbing,YANG Xiaoping,YANGXingjiang.Application and implementation of general framework based on web layering structure[J].Computer Engineering and Design,2008,29(7):1859-1862(in Chinese).[周相兵,杨小平,杨兴江.基于Web分层结构的通用框架实现及应用[J].计算机工程与设计,2008,29(7):1859-1862.]
[7]QIN Pengtao,WANG Suwen.Application of simple factory pattern in data access layer [J].Computer Engineering and Design,2009,30(7):1799-1801(in Chinese).[秦澎涛,苏文.简单工厂模式在数据访问层中的应用[J].计算机工程与设计,2009,30(7):1799-1801.]
[8]LOU Buye.Design of auto-marking software framework for Java programming questions [J].Computer Engineering and Design,2010,31(24):5343-5346(in Chinese).[娄不夜.Java编程题自动判分软件框架的设计 [J].计算机工程与设计,2010,31(24):5343-5346.]
[9]XIAO Lu,LONG Pengfei.The research of AOP based on Java dynamic proxy[J].Micro Computer Information,2011,27(2):211-213(in Chinese).[肖露,龙鹏飞.基于JAVA的动态代理实现的AOP的研究 [J].微计算机信息,2011,27(2):211-213.]
[10]DUAN Chao,QIAN Gang,SUN Yijie.Practice and application of the technology of reflection ln database operation[J].Process Automation Instrumentation,2010,31(6):17-20(in Chinese).[段超,钱刚,孙怡劫.反射技术在数据库操作方面的实践与应用[J].自动化仪表,2010,31(6):17-20.]
[11]Nagel C.Professional C#[D].6nd ed.Beijing:Tsinghua University Press,2008:10-205(in Chinese).[(美)内格尔(Nagel.C).C#高级编程[D].6版.北京:清华大学出版社,2008:10-205.]
[12]WANG Jianguo,WANG Jianyin,Struts+Spring+Hibernate frameworks and application development[D].Beijing:Tsinghua University Press,2011:1-638(in Chinese).[王建国,王建英.Struts+Spring+Hibernate框架及应用开发 [D].北京,清华大学出版社,2011:1-638.]