`
pojo
  • 浏览: 24740 次
  • 性别: Icon_minigender_1
社区版块
存档分类
最新评论

元数据、开放数据模型及动态系统--形而下学篇

阅读更多

普适数据容器



数据集(DataSet)是一个普适的,弱类型的,开放的数据容器。普适是指它可以适应任何基于名值对集合或名值表的数据形式,比如Properties,Database Tables, XML, CSV, JSON, LDAP, ...。弱类型是指所有的容器都用同一个数据结构,存放在容器中的只有两种对象:数值和子结构。数值一律用字符串表示,时间日期用XML标准形式来表示,子结构一律用数据集表示。有关定义域的元数据存放在专门的数据结构中。如果需要类型转换,借助元数据,可以很容易地做到。所以为了突出本质,我宁愿把数据转换的代码放到AccessorWrapper里。事实上在我们的项目里,根本就没有用到类型转换。所谓开放,就是前面说过的,容器可以适应数据模型的变化,以不变应万变。

下面给出两个主要的接口。DataSet继承Iterator和Map,即是为了表明概念继承上的渊源,也是为了与Web模板结合。数据集在元数据只含有字段名,并且不具有滚动性质的情形下,退化为Map。

java 代码
 
  1. public interface DataSet extends Iterator, Map  
  2. {  
  3.     public abstract String getDataSetName();  
  4.     public abstract void setDataSetName(String name);  
  5.     public abstract DataSetMetaData getMetaData();  
  6.     public abstract void setMetaData(DataSetMetaData md);  
  7.   
  8.     public abstract DataSet getDataSet(int column);  
  9.     public abstract void setDataSet(int column, DataSet ds);  
  10.     public abstract DataSet getDataSet(String path);  
  11.     public abstract void setDataSet(String path, DataSet ds);  
  12.   
  13.     public abstract String getValue(int column);  
  14.     public abstract void setValue(int column, String value);  
  15.     public abstract String getValue(String path);  
  16.     public abstract void setValue(String path, String value);  
  17. }  
  18.   
  19. public interface DataSetMetaData  
  20. {  
  21.     public abstract int getColumnCount();  
  22.     public abstract void setColumnCount(int count);  
  23.   
  24.     public abstract int getColumnDisplaySize(int column);       // optional  
  25.     public abstract String getColumnLabel(int column);          // optional  
  26.     public abstract String getColumnName(int column);  
  27.     public abstract int getColumnType(int column);  
  28.     public abstract String getColumnTypeName(int column);  
  29.     public abstract int getPrecision(int column);               // optional  
  30.     public abstract int getScale(int column);                   // optional  
  31.     public abstract boolean isAutoIncrement(int column);        // default "no"  
  32.     public abstract boolean isCaseSensitive(int column);        // default "yes"  
  33.     public abstract int isNullable(int column);                 // default "yes"  
  34.     public abstract void setAutoIncrement(int columnIndex, boolean property);  
  35.     public abstract void setCaseSensitive(int columnIndex, boolean property);  
  36.     public abstract void setColumnDisplaySize(int columnIndex, int size);  
  37.     public abstract void setColumnLabel(int columnIndex, String label);  
  38.     public abstract void setColumnName(int columnIndex, String columnName);  
  39.     public abstract void setColumnType(int columnIndex, int DataType);  
  40.     public abstract void setColumnTypeName(int columnIndex, String typeName);  
  41.     public abstract void setNullable(int columnIndex, int property);  
  42.     public abstract void setPrecision(int columnIndex, int precision);  
  43.     public abstract void setScale(int columnIndex, int scale);  
  44. }  

 

数据集的辅助工具

为了便于应用,需要有一整套的工具类。下面列出与数据集应用有关的核心类和辅助类,结合后面的用例,可以让你对数据集的应用方式有一个大概的了解。
核心接口
    DataSet
    DataSetMetaData
    DataSetException
    ...
核心实现:
    DataSetBaseImpl
    DataSetMetaDataBaseImpl
    DataType
    TypeFormat
    ...

辅助工具包:   

    SAX 2扩展包。
        DataReader < org.xml.sax.XMLReader
            DefaultReader <~ JAXP Default Reader
            DataSetReader
            DBTableReader
            DBLobReader
            JndiReader
            TeeFilter
            ObjectReader
            ResultSetReader
            ...
        DataSource < org.xml.sax.InputSource
        DataSink ~ org.apache.xml.serializer.Serializer
            DefaultSink ~ org.apache.xml.serializer.Serializer
            DataSetSink
            DBTableSink
            DBLobSink
            ObjectSink
        DataPipeFactory
    IO扩展包
        InputStreamBuffer, OutputStreamBuffer
        NullInputStream, NullOutputStream
        ByteMessageInputStream, ByteMessageOutputStream
        TextMessageInputStream, TextMessageOutputStream
        SmartZipInputStream, SmartZipOutputStream
        RewindableInputStream
        FanOutputStream
        StreamCopier
    组件框架
        略过

数据集用例

在应用上,数据集的应用模式,遵循了SAX 2的标准模式。

SAX 2的标准模式

java 代码
 
  1. XMLReader reader = org.xml.sax.helpers.XMLReaderFactory.createXMLReader();  
  2. //获取一个InputStream实例is,然后  
  3. InputSource source = new InputSource(is);  
  4. //获取一个ContentHandler实例handler,然后  
  5. reader.setContentHandler(handler);  
  6. reader.parse(source);  

数据集的例子
java 代码
 
  1. EndPoint source_ep = new EndPoint("jdbc://MyDataSource/MySchema/MyName");  
  2. DataReader reader = DataPipeFactory.createDataReader(source_ep);  
  3. DataSource source = DataPipeFactory.createDataSource(source_ep);  
  4. EndPoint sink_ep = new EndPoint("object:///net/hyperdigital/dataset/DataSet");  
  5. DataSink sink = DataPipeFactory.createDataSource(sink_ep);  
  6. DataHandler handler = sink.asContentHandler();  
  7. reader.setContentHandler(handler);  
  8. reader.parse(source);  
  9. DataSet ds = handler.getContent();  

上面这段代码将执行“select * from MySchema.MyName",并将数据读入一个数据集,数据集的名字是MyName。如果我想执行一个特定的SQL语句,我只要在上面第一行后加上一行:
java 代码
 
  1. source_ep.setProperty("read""select xxx,xxx, xxx from xxx where ...");  

相应的代码会执行给定的SELECT语句,并把结果读入一个数据集,数据集的的名字属性是MyName。

如果我想把结果存放在一个XML文件中,可以这样做:

java 代码
 
  1. EndPoint source_ep = new EndPoint("jdbc://MyDataSource/MySchema/MyName");  
  2. DataReader reader = DataPipeFactory.createDataReader(source_ep);  
  3. DataSource source = DataPipeFactory.createDataSource(source_ep);  
  4. EndPoint sink_ep = new EndPoint("file:///absolution/file/path/MyFileName.xml");  
  5. DataSink sink = DataPipeFactory.createDataSource(sink_ep);  
  6. DataHandler handler = sink.asContentHandler();  
  7. reader.setContentHandler(handler);  
  8. reader.parse(source);  

和前面的代码相比,不同的仅仅是数据汇(数据流向的终点)的定义。如果要把数据存作XML并且发到消息队列上,同样只要改动数据汇:
java 代码
 
  1. EndPoint sink_ep = new EndPoint("queue://MyQCF/MyQueue");  

XML是假定的媒介形式,我可以以通过指定端点的属性来改变媒介形式。

如果我要把数据存入数据库表,假定数据集里的字段名和数据库表中的字段名相同,(convention over configuration),相应的代码如下:
java 代码
 
  1. EndPoint source_ep = new EndPoint("object://java/net/hyperdigital/dataset/DataSet");  
  2. DataReader reader = DataPipeFactory.createDataReader(source_ep);  
  3. DataSource source = DataPipeFactory.createDataSource(source_ep, dataset);  
  4. EndPoint sing_ep = new EndPoint("jdbc://MyDataSource/MySchema/MyTable");  
  5. DataSink sink = DataPipeFactory.createDataSource(sink_ep);  
  6. DataHandler handler = sink.asContentHandler();  
  7. reader.setContentHandler(handler);  
  8. reader.parse(source);  

数据迁移是小菜一碟:
java 代码
 
  1. EndPoint sing_ep = new EndPoint("jdbc://MyDataSource/MySchema/MyTable");      
  2. DataReader reader = DataPipeFactory.createDataReader(source_ep);      
  3. DataSource source = DataPipeFactory.createDataSource(source_ep, dataset);      
  4. EndPoint sing_ep = new EndPoint("jdbc://YourDataSource/YourSchema/YourTable");      
  5. DataSink sink = DataPipeFactory.createDataSource(sink_ep);      
  6. DataHandler handler = sink.asContentHandler();      
  7. reader.setContentHandler(handler);      
  8. reader.parse(source);    

不厌其烦贴了那么多,是为了显示在发布系统中任意两个端点间的数据通讯,都是同一模式。现在让我们来看一看EndPoint是个什么东西。java.net.URI是不可继承的,所以下面extends的意思是EndPoint包括URI的所有方法。
java 代码
 
  1. public class EndPoint extends URI  
  2. {  
  3.     public EndPoint(String endpoint) {}  
  4.     public EndPoint(String context, String relative) {}  
  5.     public EndPoint(URI uri) {}  
  6.     public EndPoint(URI context, String relative) {}  
  7.    
  8.     public String getContextPath() {}  
  9.     public String getRelative() {}  
  10.     public String getTextType() {}  
  11.     public URI getURI() {}  
  12.   
  13.     public String getProperty(String name) {}  
  14.     public void setProperty(String name, String property) {}  
  15.     public Set getPropertyNames() {}  
  16. }  

主要的属性(Property)有方法(method)和文本类型(text_type)。方法可以是Read, Write, Append, Delete, Get == Reader, Insert == Put == Write, Post == Update。Read是假定的也是唯一的源方法,Write是假定的汇方法。文本类型的值可以是text, xml, html,这些是Xalan中已经实现的,也可以很容易地增加cvs, json等等类型。EndPoint里的URI,基本上都是URL。EndPoint支持的传输协议,包括所有java.netURLConnection支持的协议,再加上jdbc(数据库表和LOB),jndi, queue,topic,object。Object的主要支持字符串、数据集、Map、。。。,字符串里的内容,可以是任何文本类型(text_type)。

再回过头去看上面的代码片断,设想一下各种数据源和数据汇的组合,上面的代码,远远超过了仅仅作数据库访问的意义,它可以用于网络上任意两个组件或者服务之间的数据传输,它可以看成是一个分布式数据访问和数据传输的框架。

数据集的应用模式

对数据的操作可以有两种模式:一种是OO模式,另一种我暂且称之为数据集管道模式,是我要介绍的模式。

在管道模式看来,数据集(DataSet)代表一个向量或者一个向量的序列。那么,对数据的操作就是作用于这个向量的一个矩阵,而ContentHandler就是一个事件驱动的矩阵实现。普通的ContentHandler更适于同一空间中的线性变换(实际上不一定线性),而Transformer对应于不同空间之间的变换。数学上,矩阵是可以连乘的,在SAX 2,Transformer是可以像管道一样串连的,二者一一对应。这种管道化的特性,提供了又一个分解复杂逻辑的手段,使我们能够把复杂逻辑分步骤实现,每一个子逻辑块实现一个明确的子目标。

数据集的管道模式借用了SAX 2的管道模式。上面的几个例子显示了数据传输的应用,它们是最短的管道系统,一个输入缓冲池(DataSource),一个泵(DataReader),一个输出缓冲池(DataSink)。系统把流体(数据)从输入缓冲池运送到输出缓冲池,不做任何处理。如果要对数据进行处理,只要在泵和输出缓冲池之间接上一个或几个过滤器(XMLFilter/Transformer)就可以了。过滤器可以是自己实现的ContentHandler,也可以是由XSLT产生的Transformer。

管道模式实际上就是用XML Schema实现领域模型,用ContentHandler/XSLT实现领域逻辑的模式。管道模式的好处是:1。最弱的类型依赖,所有的数值都是字符串类型,程序员不必费心类型转换。2。最佳的动态性能。XML Schema和XSLT都是文本文件,可以动态部署,动态修改。领域模型和领域逻辑的改变后,代码不必跟着修改。坏处是你得学XML和SAX 2。较之于一大堆繁不胜繁,遮遮掩掩,修修补补而又欲盖弥彰的设计模式起来,XML要简单直白多了。另一个坏处是,XSLT的功能不够强大,不足以应付在某些复杂情况。不过,某些OO模型下看来很复杂的问题,用XSLT来实现的话,也可能很简单。

OO模式也可以由两种做法。一种是传统的做法。一种是在数据集的基础上构造领域模型,将领域模型封闭在领域逻辑层。传统的领域模型+ORM的模式,不可避免地会把领域模型的细节暴露给领域逻辑层以外,而造成系统对领域模型不必要的依赖,大大降低系统的稳定性和动态性。我对传统的领域模型+ORM产生的大量的空壳类和映射文件十分厌恶,在我看来,它们就像一具具没有灵魂,没有血肉的骷髅,可以用尸横遍野来形容。

从对数据所负的责任来看,系统的参与者(actor)可以分为两类。一类是中介(agent),一类是客户(client)。客户是数据的最终消费者,负责对数据的解读和处理加工。数据只有对客户才有逻辑的和物理的意义。而中介只是传递数据,数据对于中介来说是没有意义的毛数据(raw data),中介的责任是将毛数据不失真的传递到客户的手中。从系统的壳层(layer)结构来看,客户位于领域逻辑层内部而中介位于领域逻辑层外部,因此中介们不必要也不应该知道领域模型的细节。这才符合编程的基本道理。通常的Domain Model+ORM的模式,就是因为违背了这个基本的道理,才有了种种的弊端。

 

小结

  • 数据集是一个普适的,开放的数据容器。数据集可以用来承载任何基于名值对的数据媒介。
  • 在数据集中,数值和元数据是分离的。元数据,除了字段名以外,可以在需要的时候延迟加载,不需要就不必加载。
  • 用数据集构造的系统,在领域逻辑层外,数据被视为毛数据,不再有类型的桎梏。
  • 在纵向,数据集可以应用于系统的各层之间,以来取代大量的XO(DTO,PO,VO,...)。
  • 在横向,已经开发出的工具可以使数据方便地在组件和服务之间传递。
  • 系统的配置信息,也是基于名值对的持久数据,当然也可以用数据集以及相关的技术。
  • 简单一致的数容器和传输模式,大大简化了组件之间的连通和阻抗匹配问题,开发团队可以集中精力解决领域逻辑问题。
  • 服务数据库表和服务本地XML文件的代码几乎是一样的,所以只需截取几条典型记录,存作XML,就可以用于离线测试。
  • 因为输入输出都是纯数据,都是同一个类型,Mock Object不再需要,大大简化了单元测试。
  • 易于离线开发和离线测试。我做J2EE项目,开发中基本不需要EJB容器。结果我的离线开发工具/环境被其他团队拿去作为正式的离线工具(standalone tool)。离线系统和在线系统具有完全相同的功能,唯一的差别是单线程和多线程。
  • 用数据集构造的系统,可以实现简单项目到复杂项目的平滑过渡,或者说复杂项目各阶段之间的平滑过渡。
  • 用数据集构造的系统,符合REST和SOA的要求。这里就不展开了。
分享到:
评论
20 楼 eboge 2007-05-11  
很好, 学习中
19 楼 gqdou 2007-05-11  
我们是做银行业务的,我们的系统中数据模型是定义在xml里,然后对xml进行实例化后,将这个对象在v层,c层,m层各个层进行传递,并能随时动态修改这个对象,或者添加一个对象节点。我一直欣赏这样的设计,试图在java开源领域找到这样的设计,今天算是头一次碰到了。
18 楼 gqdou 2007-05-11  
我们是做银行业务的,楼主的这个思想与我们现在的系统中数据模型用xml来传递非常相象,我一直想从开源领域找到这样的产品,今天算了看到了,谢谢分享。
17 楼 magic_seek 2007-03-09  
<p>上午看了上篇,下午看了下篇,看完后对楼主的DateSet和DataSetMetadata两个接口感觉比较亲切,我可以想到接口里每一个方法的用途,甚至包括那个系统本身不是必须的columnLabel。不过因为我们的最终应用不同,后面的东西就大相径庭了。</p>
<p>看了各位的回复,感觉能够理解楼主本意的不是太多(我也不一定理解正确),觉得还是应该推广这个数据集的概念,因为它是非常有用的东西,而且它也是通用的,未来的编程中可能会处处用到它。</p>
<p>如果大家用过JDBC来实现一个通用的数据库管理程序(如SqlServer的查询分析器),那么一定会使用到Connection的Metaata和ResultSet的Metadata,通过Connection和ResultSet的元数据我们可以管理数据的任意一张表的任意一条数据。</p>
<p>不过数据库的表是二维的,同一张表里不能表示任意复杂数据,所以如果我们想保留通过元数据而操作任意一张表的数据的功能而且还能表示任意复杂的数据的话,那么我们可以把二维表扩展到树形结构当中,我们知道XML几乎可以表示任意一个你想要的复杂数据,所以如果ResultSet扩展到了树形结构中那么它也能表示任意复杂的数据。</p>
<p>有了以上的步骤后,那么就可以设计出类似楼主的DataSet和DataSetMetaData的两个接口了。有了接口那么就可以编写实际的应用了,这时你会发现你的程序似乎可以管理任何一个复杂的数据,而且更重要的是象实现一个通用数据管理这样的程序是不需要考虑数据所代表的含义的,也就是说可以不管这些数据的实际用途,不用去管它们是什么业务模型,所以这时完全可以为管理数据而做一套功能模块来,使用这个模块你可以忽略很多诸如数据转化和数据存储等方面的细节,你只需专心考虑最终如何处理这些数据就可以了,这样会大大提高程序的灵活性和开发效率。我想楼主的框架程序应该也是这样的。</p>
16 楼 nihongye 2007-02-20  
为什么不直接使用DOM?
15 楼 pojo 2007-02-20  
minimu:
引用

基本上模糊了
其实没有源代码无所谓,甚至不用大量介绍哪些借口什么的,但是至少给出一个清晰的应用脉络,那样容易看明白

很抱歉,让你感到困惑了。

我写那篇文章的初衷,是因为我认为DataSet的概念和相关的技术,具有比较普遍的意义,可以应用到许多领域。因为我相信它具有普遍的意义,我也相信它在数学上一定有它的道理。通过写那篇文章,我找到了一些数学上的解释,有些在文章里写了,有些没有写在文章里。

因为是具有普遍意义的东西,所以我没有选择从具象展开,而是从抽象入手。我给出的接口和罗列的类名,以及代码片断,是想给读者提供一个想象的空间。它的具体应用,取决于读者的阅历和想象力。熟悉FP的,也许可以看到一点FP的影子。做过分布系统和SOA项目的,和其它的方法比较一下,也许会同意这是一个非常简洁和高度一致的方法,高度一致意味着高度重用。

空对空的到此为止,下面来点实际的。DataSet,从Java角度看,可以把它看成Map,CachedResultSet,Java Bean,或者像ajoo指出的,看成DynaBean,DynaClass,等等。从XML的角度看,它是一个像DOM一样的数据模型(Data Model),我给它找了一个官方的模型--XDM (XQuery 1.0 and XPath 2.0 Data Model),我只支持Document Node,Element Node,Attribute Node,Text Node,不支持Processing Instruction Node和Comment Node。选这个模型是因为它和我的实现很接近,也是为了支持XPath和XQuery。XPath用JXPath来实现,很容易做到。XQuery的实现还不多,如果看见简单的XQJ的实现,我会把它集成进来。这个方向上再往下走一步,就是XQuery和XSLT的等价置换,比如将XQuery编译成XSLT。如果我能做到这一步,我就可以做许多LINQ可以做的事。这是我几天前看LINQ的视频演示时冒出来的想法。就是现在,我也可以用它做许多WebLogic Integration Server, IBM Message Broke,MS BizTalk可以做的事,用非常少的代码。

上面说的可能还是太宏观,我们再来点微观的。在我的Blog里,我开列了一个栏目--数据集用例。暂时只有一个用例。如果有人出题,或者我看见有合适的用例,我会不断地充实。我希望读者在阅读用例的时候,充分发挥你的想象力。现在的这个例子,它其实可以有许多变化。比如把输入端换成SQL,它就可以根据查询的结果生成网页。EndPoint是纯碎的申明,没有任何逻辑的,它可以是事先配置的,也可以是实时产生的。再把输出端换成HttpResponse,网页就到浏览器了,它就成了一个用XSLT作模版的Web"框架"。输入端还可以换成消息队列,代表异步处理的结果。不管你怎么换,代码还是那几句。像这样简灵活的应用,恐怕用Ruby也做不到吧。
14 楼 minimu 2007-02-20  
基本上模糊了
其实没有源代码无所谓,甚至不用大量介绍哪些借口什么的,但是至少给出一个清晰的应用脉络,那样容易看明白
13 楼 coolfish 2007-02-12  
SDO,楼主从自己的经历中发现了SDO规范中提出解决问题的方法.
12 楼 xmlspy 2007-02-09  
没明白这个是干什么用的;(
11 楼 pojo 2007-02-07  
我是把它当作简化的SDO来用的。
10 楼 partech 2007-02-06  
SDO
9 楼 jianfeng008cn 2007-02-06  
数据集的概念很类似dotnet的ado机制的嘛
8 楼 onlydo 2007-02-06  
潜水多年,忍不住大吼一声:真不赖
7 楼 LucasLee 2007-01-18  
pojo 写道

引用

总结一下,你的架构估计是为了SOA一类的大型系统设计的。我对那些并不熟悉,不过我怀疑的是你们是否值得用这种复杂的结构,不要说太远的未来,或者噱头的问题。

我们的系统已经运行三年多了。第二版是我一个人写的,就用这里说的方法,将整个系统重构。代码是第一版的一半,运行效率是第一版的4倍。第二版也已经运行两年多了,我离开那家公司都快一年了,系统照样在转。


对于你说的成就,我很欣赏。
但似乎你并没有回答我的问题。

我已经大概知道你一个人做了这个系统,然后系统性能比这个那个的商业版本高很多倍。
但是我仍然不清楚你的软件是在什么样的环境下产生的,为了解决什么为题而做。
脱离了这些,我想我们理解也困难。
先告诉我们需求是什么,再说怎么做,为什么。

我猜了一下,既然你提到了Biztalk之类的,那么你做的是否是一个完整的EAI软件呢?或者是一部分?

6 楼 歆渊 2007-01-18  
嗯, 分布的组件之间只传数据进行互操作的设计现状, 也是传统技术框架造成的, 以后 HBI (Hosting Based Interfacing) 发展起来, 传递带逻辑的数据对象就会很自然了.

从应用上来看, 这个系统结构起到的好像是 ESB (Enterprise Service Bus) 的作用, 兼有一些企业组件开发最佳实践的指导意义. 如果花心思规整包装一下, 差不多可以作为一个SOA方案卖了.
5 楼 pojo 2007-01-18  
引用

另外 数据集 也损失了 OO 理念里同时用 数据 和 行为 来建模解决问题的优势. 不过在异构系统里想全盘 OO 也是不太可能的, 所以在不同平台之间只是传递数据也不失为实践中的好方法.

我想你没有理解我的意思。我没有明言背景是分布系统。我说的是在需要模型的地方(领域层)建模,在除此以外的任何地方,数据就是(毛)数据。

引用

1.为什么一定要使用XML作为中间数据格式?既然所有使用的情形都是JAVA,为什么不用JAVA类呢?更快,更自然些。可能会说,为以后跟异构系统交互?那么是否又经过了足够的考虑呢。

我考虑的是:开放,动态,简单。有了类,就有了约束,就有了耦合,就不开放,不动态。

引用

2.我觉得用法还算比较复杂的。可能为了这个普适性,必须要付出的代价,关键是你们用到了这个普适习性么,如果是,或者你们的应用就是要这么复杂,我觉得尚可理解。

我们的项目是一个类似MS BizTalk或BEA Integration Server的项目。我们实现的是一个引擎,下游的团队用它来开发他们的项目。随着下游项目的加入,Domain Model就要修改,平均一个月改一次。所以项目的性质决定了它必须是动态的开放的。在项目的初始阶段,我们请了BEA来做POC,结果Integration Server不能满足我们的动态需求,另外运行效率也不好。BizTalk也一样。这之后,我们才决定自己做的。

最后的结果是,我们不仅满足了系统对于需求的稳定性,而且我们的系统至少比BEA Integration Server快25倍,比BizTalk快15倍。

那些例子,就是REST Style的WebSerivce。如果你觉得用法复杂,那是我写作的失败。

引用

总结一下,你的架构估计是为了SOA一类的大型系统设计的。我对那些并不熟悉,不过我怀疑的是你们是否值得用这种复杂的结构,不要说太远的未来,或者噱头的问题。

我们的系统已经运行三年多了。第二版是我一个人写的,就用这里说的方法,将整个系统重构。代码是第一版的一半,运行效率是第一版的4倍。第二版也已经运行两年多了,我离开那家公司都快一年了,系统照样在转。

4 楼 LucasLee 2007-01-18  
看完了上下篇。有几点感想及疑问。
1.为什么一定要使用XML作为中间数据格式?既然所有使用的情形都是JAVA,为什么不用JAVA类呢?更快,更自然些。可能会说,为以后跟异构系统交互?那么是否又经过了足够的考虑呢。
2.我觉得用法还算比较复杂的。可能为了这个普适性,必须要付出的代价,关键是你们用到了这个普适习性么,如果是,或者你们的应用就是要这么复杂,我觉得尚可理解。
3.我认为用XML做持久层来替代DB,以实现离线测试的想法难以理解。如果仅是存、取单条记录,做这类非常简单的测试那自然可以,但是对于给出SQL稍复杂的查询应该就无能为力了,如果非要做什么离线或者简化测试,似乎不如用一个HSQLDB来做,(当然,这并不是一个简单的路子,我觉得还是在线测试好了)

总结一下,你的架构估计是为了SOA一类的大型系统设计的。我对那些并不熟悉,不过我怀疑的是你们是否值得用这种复杂的结构,不要说太远的未来,或者噱头的问题。

另外一点意见,我对你表述的语言有点搞不清,比如什么矩阵连乘什么的(一看就是学院派的,高!),我的数学不好,是不是可以用简单直白的语言表达呢?搞个大伙都明白的方法。
3 楼 歆渊 2007-01-17  
我在 WoW http://wow.dev.java.net 里实现了一个  XT (XML Based Transmission) 机制, 可以通过 XML 流传递对象, 这样就同时承载了 数据 和 逻辑. 不过编程接口不是那么友好, 需要应用类实现 XSendable 和 XReceivable 接口, 也要先理解掌握 SAX 原理, 再去在适当的Method里实现传递逻辑.

public interface XSendable
{
    /**
     * The xid must be valid xml local tag name.
     */
    String getXID();

    void send2(XCarrier carrier) throws SAXException;
}

public interface XCarrier
{
    void sendable(XSendable sendable) throws SAXException;

    void startElement(String name) throws SAXException;

    void attribute(String name, String value) throws SAXException,
            IllegalArgumentException;

    void text(String text) throws SAXException;

    void endElement(String name) throws SAXException;
}

public interface XReceivable
{
    /**
     * The xid must be valid xml local tag name.
     */
    String getXID();

    void startReceivingFromX();

    void receiveXAttribute(String name, String value);

    void startReceivingXContent();

    void receiveXText(String text);

    void startReceivingXNestedElement(Stack<String> elements);

    void receiveXNestedElementAttribute(Stack<String> elements, String name,
            String value);

    void startReceivingXNestedElementContent(Stack<String> elements);

    void receiveXNestedElementText(Stack<String> elements, String text);

    void endReceivingXNestedElement(Stack<String> elements);

    XReceivable mapNestedXReceivable(String xid, Stack<String> elements);

    void nestedXReceivableStartedReceiving(XReceivable receivable);

    void nestedXReceivableEndedReceiving(XReceivable receivable);

    void endReceivingFromX();
}


在两边同是Java环境的情况下逻辑上比较清晰, 比如这个 请求即时消息存档 的任务对象:
public class RequestArchivedIMsgs extends Submission
{

    // shared code

    private transient long tid;

    private transient long beforeID;

    private transient long beforeTime;

    private transient int max;

    // traverser side code

    public RequestArchivedIMsgs(long tid, long beforeID, long beforeTime,
            int max)
    {
        this.tid = tid;
        this.beforeID = beforeID;
        this.beforeTime = beforeTime;
        this.max = max;
    }

    @Override
    protected void sendAttributes2(XCarrier carrier) throws SAXException
    {
        super.sendAttributes2(carrier);
        carrier.attribute("tid", String.valueOf(tid));
        carrier.attribute("beforeID", String.valueOf(beforeID));
        carrier.attribute("beforeTime", String.valueOf(beforeTime));
        carrier.attribute("max", String.valueOf(max));
    }

    // scener side code

    public RequestArchivedIMsgs()
    {
    }

    @Override
    public void receiveXAttribute(String name, String value)
    {
        if ("tid".equals(name))
            tid = Long.parseLong(value);
        else if ("beforeID".equals(name))
            beforeID = Long.parseLong(value);
        else if ("beforeTime".equals(name))
            beforeTime = Long.parseLong(value);
        else if ("max".equals(name))
            max = Integer.parseInt(value);
        else
            super.receiveXAttribute(name, value);
    }

    @Override
    public void realize() throws AccessDeniedException, TheObjectException
    {
        Persistent<? extends TheTopic> topic = scener.getApp().getTOB()
                .get(tid);
        if (topic == null || !(topic.o instanceof TheTopic)) return;
        scener.getConnection().postGuidance(
                new SendArchivedIMsgs(
                        scener.getUserAccount().o.getOwningUser(), topic,
                        beforeID, beforeTime, max));
    }

}

它发送到服务器端执行时, 其实是构造一个 SendArchivedIMsgs 对象, 也是通过 XML 流传递回去, 在客户端把自己承载的数据填入到客户端缓存, 然后激发用户界面更新.

对动态结构化的数据, SAX 方式的传递和处理是最高效的. 另外因为是XML, 也有可能用于异构系统.
美国那边已经有开发者在用Flash做WoW的浏览器端界面, 也算是 XT 在异构系统上的尝试的起步了.

楼主如果有兴趣可以下载 WoW 的源码看看, 感兴趣的问题共同研究讨论.
2 楼 歆渊 2007-01-17  
嗯, 可以看出来这个架构有很强的XSLT传承, 非常适合 流式的 批量 数据处理.

不过在OLTP方面可能就相对比较弱, 数据集 因为本身只是数据载体, 缺少 数据库管理系统 的 事务 和 并发控制 机制, 对 数据集 的复杂 统计 和 查询 效率上也会有障碍, 如果说改为通过数据库进行查询, 查询结果先封装成结果集再由程序处理的话, 那就回到ORM的思路上去了, 而且效果可能还不如ORM.

另外 数据集 也损失了 OO 理念里同时用 数据 和 行为 来建模解决问题的优势. 不过在异构系统里想全盘 OO 也是不太可能的, 所以在不同平台之间只是传递数据也不失为实践中的好方法.
1 楼 smilelee74 2007-01-17  
DataSetMetaData也相当于一个配置文件了

相关推荐

Global site tag (gtag.js) - Google Analytics