public property get account() as object
on error goto errorhandler
dim engaccount as object
dim errornum as long
if mcolaccount is nothing then
if not safecreateobject(engaccount, obp_account_eng, _
errornum) then
err.raise err_accountsafecreatefailed, _
"iobpuserinfoeng.account property", "unable to " & _
"create " & obp_account_eng & ". return code: " & _
errornum
goto cleanexit
end if
'只获取未删除的记录(默认条件)。
set mcolaccount = engaccount.search(securitytoken := _
securitytoken, usernumber := me.usernumber)
end if
set account = mcolaccount
<...>
end property
再仔细看一下:首先,检查内部变量看当前是否有引用指向 account collection。如果有,则返回此 collection;否则,调用 account engine 的 search 方法来返回与当前的 usernumber 相关联的一个或多个 account。这意味着对于 coluserinfo collection 中的每个 cuserinfo class,都将有一个特定的 account 或多个 account — 对应该单个 usernumber; 换句话说,一个层次结构。此外,在调用 account 方法之前,cuserinfo 中并不存在 account collection,因此实现了按需要加载。因为对 search 方法的内部调用只完成了一次,所以像 refresh 这样的参数可以被添加到该对象的接口中。这就允许动态重新加载内容而无须破坏和重新创建 cuserinfo。这说明构建在 ecc 中的可重用性和层次结构中的松散耦合对象的价值。它还允许以最小的影响更改实现的细节。用户永远不会知道在这个罩盖之下发生的一切。一个样例用户的代码行如下所示:
straccountnumber = coluserinfo.item(1).account.item(1).number
准确了解在此行中发生的情况是重要的。首先,假定您已经创建了一个称为 coluserinfo 的 userinfo collection,其中每个用户至少有一个账户。第一步,coluserinfo.item(1),调用 item 方法并返回与该用户对应的 cuserinfo class。下一步,.account,创建一个 account engine,此 engine 又创建一个 account collection,并向其中置入一个或多个 caccount class,然后返回此 collection。再下一步,.item(1),调用 account collection 的 item 方法,此方法返回第一个 caccount class。最后一步,.number,调用 number property get 方法并返回带有此账户编号的一个字符串,该字符串又被赋给 straccountnumber 变量。
获取一个特性需要做大量的工作,访问该特定对象的每个特性都意味着为所访问的每个特性创建一个 collection 和 class。在这种情况下,将此代码写为如下的形式较好:
dim colaccounts as colaccount
set colaccounts = userinfo.item(1).account
straccountcode = colacounts.item(1).number
strphone = colaccounts.item(1).custphone
strname = colaccounts.item(1).custname
使用这种方法,就避免了对 userinfo 的 account 方法所进行的几个调用的开销。也可能使用一个 for..each 结构。当仅访问一个账户时,可以编写以下的代码:
dim objaccount as caccount
set objaccount = userinfo.item(1).account.item(1)
straccountcode = objaccount.number
strphone = objaccount.phone
strname = objaccount.custname
这是非常有效的,并减少了为每个账户属性多次调用 items() 方法的开销。它同样应该提供最佳的性能。
collection 的设计
collection 是 class 对象的容器,也是内部存储方案的包装。大多数面向对象的开发人员,特别是 visual basic 开发人员应该熟悉 collection/class 模型。在 ecc 模型中,collection 的主要责任是:
- 提供一个存储 class 的容器
- 将接口与容器的实现细节分开
- 为操纵容器中的 class 提供服务
- 保持 class 的信息
- 连接其它对象来模拟复杂的关系或层次结构(可选)
可以用许多不同的方法来实现 collection 的内部存储:数组、链接列表、记录集、xml 或者另一个公用数据存储模型。此处,重要的概念是:实际存储对于高级开发人员是隐藏的,他们总是以相同的方式访问数据,而不管它的存储形式。与 class 一样,collection 的接口和实现也是分开的,这就减少了更改实现带来的影响。
下面讨论一个存储 cuserinfo class 的样例 collection 以及 ecc 设计模式建议的其它特性/方法。visual modeler 用来生成初始图形。
注意: 该对象的前缀 col 表示一个 collection。
每个 class 都有一个单独的 collection,这样就会获得更大的灵活性,因为它允许为 class 适当地定制接口。其中的一个例子便是 add(),它要求传入特定的 userinfo 参数。

图 5. userinfo 实体的基本 collection 图
对 collection 接口的基本建议,collection 接口提供创建、读取、更新和删除 (crud) 功能,如下所示:
- add—long,public function()— 用来根据已定义的参数向 collection 中添加一个新 class。例如,此实现要求新的 userinfo class 在创建一个新 class 之前需要有四个参数。它返回新对象的索引。
注意: add() 方法应该将新 class 放在 collection 的尾部,否则现有的索引将失效。它也可以检查此项目是否事先已经存在(特定于实现的)。
- count—long,public get()— 用来返回 collection 中 class 的数目。