新年哪里也没有去,呆在家里写了几篇Blog与大家交流一下。平时工作很忙,也难得有时间写点东西。大年三十、初一各发了一往篇,还有那么多的博友陪我一起,像我一样,呵呵。
上面粗粗的介绍了ORM层、业务层。ORM主要是在数据访问,把程序从千篇一率的存储过程调用,从容易出错的Sql语句中解脱出来;业务层主要是规范业务逻辑的组织,简化事务处理,把精力用到处理业务逻辑的刀刃上。对于很小的BS软件,有这两层已经算是可以用了,但如果要考虑到集成、客户端,就会感觉只有这些还是远远不够的,数据处理的灵活性还不够,客户端界面的展示与业务逻辑层耦合的太紧密。
下面就要介绍到数据交换层、服务层、DTO层。我先从DTO层介绍吧,对于DTO层,有的朋友可能会感觉没有存在的必要,多了一层,也或许是这样。复杂度,工作量,对人的要求也高了,成本也高了,等等,都是要考虑的方面。
DTO层,也就是在服务器端和客户端进行沟通的,把业务逻辑层的数据传输到客户端,再把客户端的数据传输到服务器端,既然这样,那么在DTO与逻辑层数据Data之间就存在一个数据交换,数据交换在后面再介绍。DTO的数据要能够支持客户端的数据绑定,可能有的朋友会问,用业务层的数据不可以吗?答案是:如果是在局域网内可以,在广域网上不可以。业务层的数据存在很多数据是延迟加载过来的,在网络上传输数据通常是要求一次把比较多的数据传输过去,也就是通常的粗粒度设计,建立连接等开销太大、时延太大了。另外,客户端需要哪些数据,需要传输哪些数据,业务逻辑层不清楚,业务层只知道我能够提供哪些数据,DTO是知道我需要哪数据,两者所站的角度不同。业务逻辑层的数据相当于数据的提供者,DTO是数据的消费者。朋友们,再发挥一下想像,是不是可以这么理解,因为这个的存在,业务逻辑层设计的时候,可以不考虑界面是如何显示的,只要关心我的业务逻辑就可以,只要我能够提供这些数据就可以了,你是从一个对象里面得到的,还是从几个关联对象里面找到的,还是从别的系统里通过WebService得到的,业务逻辑层不关心,关心的是业务逻辑层可以提供这么多数据就够了,对于客户端,只关心有这么数据我可以处理,你是从哪里得到,客户端不关心,只关心我有这么多的数据可以处理。这样,就可以实现客户端与服务器端分离开发,设计的时候更重要的是针对中间的服务和要处理的数据设计,或许就是面向契约和服务设计吧。
借用SDO的一张图。
看了这张图,我估计很多人是非常熟悉的,如果从上面的观点和角度去考虑DTO的问题,那么这张图是自然而然的事情了,或许也是框架升级的一个必然趋势吧。这张图我是在2007年的9月份才看到的,但是时候完整的框架已经开始使用了,思想应该是相似的。
不过,这个DTO/SDO当时真的害的我很辛苦。
DTO要能够支持:数据绑定、历史记录、级联触发、合并集合、序列化与反序列化,要实现一个大的递归,从其中的任何一个对象开始,能够找出整个传递的所有数据。
1、 数据的绑定支持。要实现IEditableObject接口,对于状态的变化要实现INotifyPropertyChanged接口,INotifyPropertyChanged接口也是微软在2.0里面新添加的,估计也是为了实现数据的双向绑定而专门添加的。实现了这两个接口,微软会自动调用的。对于容纳这些数据的集合也要支持绑定处理,则要IBindingList实现接口,这样才能够实现数据的触发,这样就要实现一个对象能够找到其所有的集合,一个集合要能够其所有的对象,这样一样,就必须实现从对象、集合、对象的递归,再深入一点,就实现了数据的级联触发,实现了双向数据触发、绑定。数据源的每个属性变更,要能够在界面上反映出来,绑定界面的改变,能够记录到后台的数据源,并且要能够支持回滚。
2、 对于历史记录。当实现IEditableObject这个接口,则必然实现了历史记录,对于SDO来说,就是ChangeSummary。能够找到集合的所有更改的记录,包括删除的记录。对于单个对象而言是能够得到历史记录和当前数据,对于集合而言,则能够找出差异集,同时,集合也要具备集合回滚的功能。
3、 级联触发,则是在实现数据绑定的基础上的,对于常见的主从结构,当从对象的一个属性改变了,要能够自动通知其所在的集合、集合再通知所在的对象数据改变了,如果是多层结构,则能够自动的从最底层开始一路通知到顶层。
4、 合并集合、集合的处理。集合的处理与数据绑定是直接相当的。数据源找出差异集之后,要对差异集进行处理,处理完之后,要对原始集合进行更进一步的处理,要把两个集合进行合并处理,主要是为了同步引用。
5、 同时,另外要实现的就是Data与SDO之间的转换以及其他格式的数据与SDO、Data数据之间的数据转换,比如Binary/Xml/Text/Key/文件/等,最基本的是SDO与Data之间的转换,这些转换根据需要,有时候是单向的,有时候是双向的,根据业务需求而定。
6、 序列化与反序列化,主要是涉及到Binary和XML序列化。对于Binary序列化,倒还是相对好处理点,对INotifyPropertyChanged的处理和ListChanged的处理要多花点心思就可以了。
Binary反序列化时有效了。
private void OnDeserialized(StreamingContext context)
{
IList<T> list = base.Items;
for (int i = 0; i < list.Count; i++)
{
T t = list[i];
t.Attach(this);
SetItem(i, t);
}
OnListChanged(new ListChangedEventArgs(ListChangedType.Reset, -1));
ListChanged += delegate(object sender, ListChangedEventArgs e) { OnDataChanged(); };
}
在反序列化时要重建绑定,并且重新关联事件触发链。因为是不能进行序列化的,我的处理是在此时行了一个投机取巧,请朋友们能给点建议。当然,这只是针对
对于XML的序列化,遇到的问题还要大一些,因为Binary序列化,是对Field进行的,不会影响到事件的触发,也就不会影响到状态的改变,再者也不会引起递归调用了。但是对于微软提供的XML序列化,在此处的应用不能直接的用了,因为XML是对属性的处理,首先,会引起递归调用,其次,事件的触发会引起状态的改变,要对数据进行XML序列化与反序列化,则必须要打破递归调用和取消事件的触发,当然还要提供一些对于XML处理的常用函数了。通常是要实现自定义的对象图序列化反序化,在中间打断递归,对于状态的触发,则要在对象的底层加一特殊处理,阻止事件的触发。不过对于XML序列化反序列化,我还没有实现,上面的只是个思路,不一定正确。主要是因为现在的远程数据走的都是压缩的二进制,当然用的是Binary序列化,要做的是对Binary序列化进行优化。等BS结构提上日程的时候,再对这方面进行处理,会提供一组JS的函数,用于处理绑定、触发的,如果实现的话,则BS的界面也可以实现绑定处理了,只是这是后话了,至少要半年之后。
DTO方面,详细的可以参考Java阵营的SDO,再加上俺们阵营的数据绑定,综合起来,应该差不多了。
我的这个DTO/SDO的实现,已经在两个项目里面已经得到了应用,感觉是大大降低了客户端的代码量、难度、出错概率,配合代码工具,客户端的工作量是成倍的降低了。特别是出错概率,原来的项目,客户端的一个界面通常要调试很多次,而且调试过有不少地方还会出错,现在这方面已经算是非常友好了。在新的项目里面,还升级了的客户端框架,加入了变更通知,就是当关闭窗体的时候,后台判断数据是否改变,给出提示,这个功能是在客户端框架里提供的,编码的时候不会体现出来。这些在后面会慢慢的介绍的。
又写了这么多,希望大家能够共同讨论一下,给俺点建议呀…