UML软件工程组织

分拆与组装解决COM+的通讯问题(2)
作者:Mac 本文选自:赛迪网 2003年02月10日

 

使用分拆和组装技术


在前面我们说过,层间通讯时尽量避免使用对象,而应使用相应的替代物如字符串之类,但是并没有交代如何去解决这个问题。其实很简单,可以先将该对象所有的属性分拆出来,组成一字符串(我们称之为属性字符串),传递该字符串,然后在接收端根据这些属性字符串重新组装对象。因为传递单个字符串的开销要远远小于传递整个对象的开销。这有点类似于走私分子走私汽车时的做法,先将整辆汽车拆成零件,然后偷运入境,最后在境内重新组装销售。该方法和使用XML字符串在原理上非常相似,都是遵循了我们上面讨论的基本原则中的第一点。

在该解决方案中的关键工作就是需要合理和有效地分拆和组装对象,如果这两个环节解决不好,照样会使整个传递过程的性能下降,反而不如直接来传递对象。下面我们就以在VB6.0中为例来讲解一下该如何具体实现它。

我们知道,VB中LSet语句可以实现用户自定义变量之间的内存拷贝,所以我将采用它来实现关键的分拆和组装过程。对于每一个类对象我们设置两个对应的用户自定义类型(User Defined Type UDT):属性UDT和Buffer UDT。它们的基本形式如下:





从它们的命名和定义,我们可以很明显地知道它们各自所扮演的角色。属性UDT是和类对象相对应的,其中每个变量分别保存着类对象的属性值。而Buffer UDT则是和属性UDT一一对应的,并且与其具有同等大小,它只包含一个变量。对于简单的对象,我们可以直接利用LSet在属性UDT和Buffer UDT之间进行内存拷贝即可获得类对象的属性字符串,即Buffer中保存的值。

而对于那些包含其他子对象的集合对象,我们需要将每个子对象分别转化为属性字符串,然后将它们链接在一起,同样接收端也需要一个相对应的逆向过程。为此,我们特意编写了一个Buffer类来实现该功能。

Buffer类

从上面的描述我们可以发现,Buffer类其实非常类似于集合(Collection)对象。它能够以字符串的形式返回它的内容,同时能够用一字符串来填充它的内容。此外,还需要提供操作每个子对象的能力。为此,我们为Buffer类设计了以下一些主要功能:

Sub Add(Item As String, [Key As String]): 
向buffer中增加一个item
Property Count As Long: [Read Only]
返回当前buffer中item数目
Property CurrentPosition As Long: [Read Only]
buffer中的当前位置
Property GetState As String: [Read Only]
返回buffer的内容字符串
Sub Initialize(Length As Long, EstimatedCount As Long, Database As String, 
[AutoSort As Boolean=True]): 
初始化buffer,包含以下参数:
Length As Long: 
插入buffer中单个item的长度[len(udtData)]
EstimatedCount As Long: 
item数的最大估计值
[AutoSort As Boolean=True]: [Optional]
插入item时是否进行自动排序
Property Item(Index) As String: 
通过索引值获得item内容值
Property Key(Index As Long) As String: [Read Only]
通过索引值获得item的唯一标识Key
Property Length As Long: [Read Only]
Buffer中所有item的总长度
Sub Remove(Index): 
删除Index所代表的item
Sub SetState(Buffer As String): 
利用属性字符串来填充buffer
Property Size As Long: [Read Only]
Buffer的长度


由于Buffer类在整个系统中使用频率极高,我们选择使用VC++来编写我们的Buffer类,这样可以有效地提高整个系统的性能指标。

解决了这些技术细节以后,我们从整体上来看看系统中Buffer类的具体应用。首先,在服务器端组件的初始化过程中,属性UDT负责从数据库中获取数据并且拷贝给Buffer UDT,然后利用Buffer类的Add方法来将Buffer UDT中的属性字符串插入到Buffer中,一直重复该过程直至最后一条记录。接着,Buffe类利用GetState方法获得整个类集合对象的属性字符串并将之传递给客户端对象。客户端对象就利用该字符串来初始化本地的Buffer类(调用它的SetState方法),以及本地对象。



图一:整体架构图


例子

在上面的讲解中,我们做足了思想动员工作,我想我们还是得真刀实枪地来点实际的。由于单个对象的传递过程比较简单,我们在这里仅以类集合对象为例来讲解。首先在客户端组件(也就是下面的clInvoices Object)的Load方法中,创建一服务器端组件(srvInvoices Object)实例,并调用它的Load方法。我们看到,服务器端组件对象的属性字符串是通过参数strDetails传回到客户端的。

Set m_oSrvInvoices = New srvCSScreen.srvInvoices
Call m_oSrvInvoices.Load(CustNo:=CustNo, _ 
Details:=strDetails, _ 
ConnectionString:=m_oClContext.DSN, _
LastOnly:=LastOnly)
Set m_oSrvInvoices = Nothing
Call SetState(Buffer:=strDetails)


接着,客户端组件调用它自己的SetState方法(和Buffer类相似,所有服务器端组件、客户端组件都具有自己的SetState、GetState方法)。

接着,我们来看看在SetState方法中发生了些什么。首先,初始化本地Buffer类对象,然后利用该对象来循环初始化客户端的对象oClInvoice,最后组装成集合对象m_colClInvoices。

Set oBuffer = New Buffer
Call oBuffer.SetState(Buffer:=Buffer)
For lngIndex = 1 To oBuffer.Count
Set oClInvoice = New clCSScreen.clInvoice 
With oClInvoice
.SetAsChild.SetState
oBuffer.Item(lngIndex)'注意clInvoice类与clInvoices类的SetState方法的区别
End With 
m_colClInvoices.Add Item:=oClInvoice, _                          
Key:=Str$(oClInvoice.ID)
Set oClInvoice = Nothing 
Next
' Free Resources 
Set oBuffer = Nothing


在上面的过程中客户端首先是调用了服务器端组件的Load方法,那我们就来详细分析一下该方法:

首先调用Buffer类的Initialize方法来初始化Buffer类,其中MAX_ITEMS常量表示你希望Buffer中存储的最大item数,比如说10个invoices。接下来的循环则是从数据库中获取记录来装载udt和Buffer类。

Set oBuffer = New BufferCall oBuffer.Initialize(Length:=Len(udtData), _  
    EstimatedCount:=MAX_ITEMS, _Database:=ConnectionString) 
    Do While Not adoRSGet.EOF And Not bExitLoop 
    With udtProps
    .ID = adoRSGet!InvoiceNr  
    'Other element assignments  
    End With  
    LSet udtData = udtProps 
    OBuffer.Add udtData.Buffer 
    If Not (LastOnly) Then
    adoRSGet.MoveNext 
    Else
    bExitLoop = True
    End If
    LoopExit_OK:
    Details = oBuffer.GetState


至此,客户端成功地获得数据,可以对这些数据进行各种操作。在这个过程中,可能会发生数据的变化,这个时候就需要将这些变化更新到数据库中。此时,我们就需要一个和上述相逆的过程,将数据从客户端传递到服务器端,由服务器端组件来负责数据库的更新。

这样,我们就在客户端与服务器端形成了一条纽带,通过属性字符串来代替对象的传递。而且利用高效的Buffer类来操纵这些属性字符串,达到了即高效快捷又方便易用的效果。

总结


本文最初的灵感来自于生活中的一些感悟,主要讨论了多层架构下层间传递类对象(主要是类集合对象)的问题。想不到平淡的生活给我上了生动的一课,希望你也能够发现它的精彩。

参考

1)更多关于编写COM+应用系统的有益建议,可以参考MSDN中Edward A. Jezierski的文章"COM+ Application Guidelines for Visual Basic Development"以及David S. Platt的文章"COM+ and Windows 2000: Ten Tips and Tricks for Maximizing COM+ Performance"。

(责任编辑 Sunny

 



版权所有:UML软件工程组织