XML驱动开发
这个题目取得大了点。本文主要是想讨论一下如何借助XML的力量抽象代码逻辑,将表层应用逻辑抽象到XML配置文件中,从而让底层开发人员集中精力到底层代码的编写和优化中,把业务细节交给业务逻辑编写人员使用XML来编写。
在XML之前如果要定制应用逻辑无非通过两种方法:
1.INI文件、自定义格式文件、数据库等传统配置方式。这些方式表现力弱,并且必须编写程序检查配置合法性;
2.VBScript,JavaScript等脚本语言定制方式。此方式开发复杂,对应用逻辑编写人员的要求高。
Xml强大的表现力提供了极好的描述对象的方法,我们的思路就是从这里出发,首先对业务对象建立一个静态的模型描述,不是用UML,而是用XML架构(Shema)。 然后底层开发人员根据这个架构开发程序,同时业务逻辑开发人员也就可以依据此XML架构和业务逻辑编写XML配置文件。由于Xml简单易用,再加上Xml架构已经对业务逻辑作了框架性的抽象,所以对业务逻辑人员的技能要求比较低,而且速度以及最终结果的bug率要明显低于传统方式。笔者曾用此思路开发过的一个复杂系统,将绝大部分的应用逻辑均分布在300多个XML配置文件中,使系统能够轻松自如的应付客户剧烈的需求变更,开发成本、维护成本均有十分显著的降低。
以下我们以一个简单的例子说明一下这种方法:
假设我们需要编写一个文件操作工具,该工具能够进行一系列文件操作,如拷贝、删除、移动等等。
分析这个简单需求, 如何拷贝文件可以认为是底层的技术性逻辑,而具体拷贝哪个文件则可以算是表层的业务性逻辑。技术性逻辑变化可能性很小,而业务性逻辑的变化可能性就大得多。把这个逻辑抽象出来,放到XML配置文件中,那么修改起来就很容易了。
可以抽象为下面的XML模式:
<?xml version="1.0" encoding="utf-8" ?>
<xs:schema targetNamespace="http://woodhead.cnblogs.com/batch_op.xml" elementFormDefault="qualified"
xmlns="http://woodhead.cnblogs.com/batch_op.xml" xmlns:mstns="http://woodhead.cnblogs.com/batch_op.xml"
xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:complexType name="operation">
<xs:sequence>
<xs:element name="param" type="op_param" maxOccurs="unbounded" minOccurs="0" />
</xs:sequence>
<xs:attribute name="name" type="xs:string" use="required" />
<xs:attribute name="describe" type="xs:string" />
</xs:complexType>
<xs:complexType name="op_param">
<xs:sequence></xs:sequence>
<xs:attribute name="key" type="xs:string" use="required" />
<xs:attribute name="value" type="xs:string" use="required" />
</xs:complexType>
<xs:element name="batch_operation">
<xs:complexType>
<xs:sequence>
<xs:element name="operation" type="operation" maxOccurs="unbounded" minOccurs="0" />
</xs:sequence>
<xs:attribute name="describe" type="xs:string" />
</xs:complexType>
</xs:element>
</xs:schema>
该架构要求Xml节点中必须包含一个"batch_operation"节点作为根节点,该节点只有一个可省略的describe属性,Xml文件编写人员可以对此文件作一个简单的描述。
根节点下面可以有若干个按顺序排列的operation节点,每个节点表示一个操作,每个操作均有一个名字表示操作的类型(如拷贝、删除等)。这个节点同样有一个可省略describe属性,在Xml节点中加入描述性的节点是个很好的习惯。
每个operation节点下可以有无限多个param类型的节点,每个节点表示一个参数如文件名等等。
架构的设计至关重要,所以我们先尝试按照这个架构写个XML文件看看:
<?xml version="1.0" encoding="utf-8" ?>
<batch_operation xmlns="http://woodhead.cnblogs.com/batch_op.xml">
<operation name="拷贝" describe="将c:\t.txt拷贝为d:\t.txt">
<param key="源文件名" value="c:\t.txt"></param>
<param key="目标文件名" value="d:\t.txt"></param>
<param key="是否覆盖" value="是"></param>
</operation>
<operation name="移动" describe="将d:\t.txt移动为d:\x.txt">
<param key="源文件名" value="d:\t.txt"></param>
<param key="目标文件名" value="d:\x.txt"></param>
<param key="是否覆盖" value="是"></param>
</operation>
<operation name="删除" describe="将d:\x.txt删除">
<param key="目标文件名" value="d:\t.txt"></param>
</operation>
</batch_operation>
很简单,一目了然,不用做更多说明就能看懂。只要可能,我喜欢用中文来写配置文件甚至程序,这样对降低开发团队的交流成本而言是很有好处的。也许你对自己的英文很自信,但团队中总是会有别人向系统中加入莫名其妙的单词和令人发疯的汉语拼音。
确认架构OK后,我们可以着手编写程序了。
本文主要讨论"XML驱动"设计思路,所以程序从简编写。UML图示如下:
操作被抽象为一个接口"IBatchOperation", 针对每一种具体的操作写一个实现此借口的类,然后将此类的一个实例加入到管理器对象"COperationMgr"中。执行时遍历XML文档中的每一个“operation”节点,将节点交给管理器对象"COperationMgr"执行,"COperationMgr"通过比较“operation”节点的"name"属性和操作借口的"getName"方法返回值的方式寻找到对应的操作接口,然后将XML节点"operation"传给该接口执行操作。
代码如下:
IBatchOperation:
public interface IBatchOperation
{
string getName();
void execute(System.Xml.XmlNode xnOperation);
}
COperationMgr:
public class COperationMgr
{
System.Collections.ArrayList m_arrOp;
System.Xml.XmlNamespaceManager m_xmlNSMgr;
public COperationMgr()
{
this.m_arrOp = new System.Collections.ArrayList();
this.m_xmlNSMgr = new System.Xml.XmlNamespaceManager(new NameTable());
this.m_xmlNSMgr.AddNamespace("op", "http://woodhead.cnblogs.com/batch_op.xml");
}
public void registOp(IBatchOperation ifcOp)
{
if (this.m_arrOp.IndexOf(ifcOp) >= 0)
{
return;
}
this.m_arrOp.Add(ifcOp);
}
public void executeOperation(System.Xml.XmlNode xnOp)
{
foreach(IBatchOperation ifc in this.m_arrOp)
{
if (ifc.getName() == xnOp.SelectSingleNode("@name", this.m_xmlNSMgr).Value)
{
ifc.execute(xnOp);
return;
}
}
throw new ApplicationException(string.Format("操作不被支持。(xml:{0})", xnOp.OuterXml));
}
}拷贝操作代码如下(其他的操作就不列入了,参考拷贝操作)
public class CCopyOperation: IBatchOperation
{
System.Xml.XmlNamespaceManager m_xmlNSMgr;
public CCopyOperation()
{
this.m_xmlNSMgr = new System.Xml.XmlNamespaceManager(new System.Xml.NameTable());
this.m_xmlNSMgr.AddNamespace("op", "http://woodhead.cnblogs.com/batch_op.xml");
}
IBatchOperation 成员
}
调用的代码如下:
private void execXMLFile(string sFile)
{
COperationMgr opm = new COperationMgr();
opm.registOp(new CCopyOperation());
opm.registOp(new CMoveOperation());
opm.registOp(new CDeleteOperation());
System.Xml.XmlDocument xd = new System.Xml.XmlDocument();
xd.Load(sFile);
System.Xml.XmlNamespaceManager mgr = new System.Xml.XmlNamespaceManager(xd.NameTable);
mgr.AddNamespace("op", "http://woodhead.cnblogs.com/batch_op.xml");
System.Xml.XmlNodeList xl = xd.SelectNodes("op:batch_operation/op:operation", mgr);
foreach (System.Xml.XmlNode xn in xl)
{
opm.executeOperation(xn);
}
}
from:http://www.cnblogs.com/woodhead/archive/2006/01/07/312926.html


浙公网安备 33010602011771号