分布式环境下对具有对象类型的属性的对象的添加、删除及修改
1. 问题描述要讨论问题首先要把问题说清楚才好讨论,下面是我们所要讨论的问题的描述。注意,我们这里不讨论对象传输时的marshal及unmarshal问题,总之,对象能够在网络上传输(比如通过RMI或WebServices等)。
1.1 问题有时候对象的逻辑结构比较复杂,它不但有整型、字符串型等基本类型的属性,还包含对象类型的属性,对于处于分布式环境下的此类对象,进行对象的添加、删除及修改。
1.2 需要考虑的各个要素对于这个问题,有以下几个方面需要考虑[K1]
l 从业务逻辑的角度来看,由于此类对象就是应用程序应该处理的一个单元,所以,对于对象的操作应该是原子化的。而从用户操作的角度看来,应该在一个界面中就提供操作整个对象的能力,当然包括其下层对象。
l 如果服务器提供支持,允许客户端自己控制事务,那么,客户端可以保证自己所做的操作的原子化,但服务器要维护这个客户的状态;并且客户端在事务之间(已启动事务,但尚未提交或回滚)有可能由于某种原因失去于服务器的连接,服务器要处理这种情况有一定复杂度。
l 一般来说,为了提高吞吐量及容错性,服务器应该尽量是无状态的。
l 如果把整个对象信息包装在一个方法调用中传给服务器,那么对象定义可能会相当复杂,或者带来许多冗余工作(后面会逐一分析)。
1.3 示例这里是一个例子,Application对象包含下层对象Parameter,如下图
注意此图只是用来表示对象间的关系,并不是对象的准确定义。
1.4 难点所在当添加或删除此类对象时,问题不大。问题是,当用户对对象的下层嵌套对象做了修改以后,应该以何种方式把改变传给服务器,服务器应该以何种方式抽取对象的改变信息,并把改变更新到数据库。
2. 可选解决方案对于此问题,我们有以下实现方法,下面逐一分析。
2.1 把对对象的操作拆分开我们可以在用户界面上把对对象的操作拆分开,每一次方法调用只操作对象的一部分,要么是对象的基本信息,要么是其下层对象。此时对象本身只定义基本信息,其下层对象通过调用服务器上的方法取得,如以下伪代码(java风格):
//Parameter的类定义
class Parameter {
private String id;
private String name;
private String description;
//省略了getter和setter方法
}
//Application的类定义
class Application {
private String id;
private String name;
private String description;
private String definition;
//省略了getter和setter方法
}
//服务器上定义的方法,此处略去可能抛出的了异常
………………
public Application[] getApplications();
public void addApplication(Application app);
public void updateApplication(Applicaton app);
public void deleteApplication(Application app);//或者只传id
public Parameter[] getParamForApp(String appId);
public addParamToApp(String appId, Parameter param);
public updateParamForApp(String appId, Parameter param);//也可能不需要传入appId
public deleteParamFromApp(String appId, Parameter param);//
………………
2.1.1 优点l 逻辑清晰,实现起来已很简单,用户的每个操作,都能很快得到反映,不会出现数据不一致的现象。
2.1.2 缺点l 服务器接口上定义了大量小粒度的方法。
l 与服务器交互频繁,每次仅传输少量信息。
l 把逻辑上的一个对象人为的拆开,强迫用户做多次操作,不友好。
2.2 删除下级对象然后重新插入
由于用户所关心的仅仅是结果,我们只要保证用户操作成功后,对象的状态是用户所需要的状态就行了,用户不会关心我们是怎样实现的。所以,我们可以把对象定义成符合其逻辑定义,当需要更新对象时,仅对对象基本信息做update操作,对下层对象则采取先删除所有下层对象,然后在根据传入的信息重新生成一遍的方法。如以下伪代码:
//Parameter的类定义
class Parameter {
private String id;
private String name;
private String description;
//省略了getter和setter方法
}
//Application的类定义
class Application {
private String id;
private String name;
private String description;
private String definition;
private Parameter[] params
//省略了getter和setter方法
}
//服务器上定义的方法
………………
public Application[] getApplications();
public void addApplication(Application app);
public void updateApplication(Applicaton app);
public void deleteApplication(Application app);
………………
使用此方法,客户端会暂时维护对象修改后的状态,于服务器端的交互会少些,但用户的操作不能马上得到反映,可也不会出现用户所做的工作被丢掉的现象;比如,系统中已经有一个name为appOne的Application,用户又修改某个别的Application的name属性为appOne, 并对此Application做了许多别的修改(比如添加了许多Parameter),假如业务上不允许有同名的Application存在,那么当用户确定提交其修改时,系统可以给出提示,告知用户不能使用此名字,用户可以按照提示把name属性改为合适的值后再提交,其对Application所做的其它修改不会丢失。
2.2.1 优点l 保留了对象逻辑上的完整性
l 逻辑清晰,实现简单,对对象的修改和添加对象很相似。
2.2.2 缺点l 虽然不会出现大量小粒度的方法调用,但可能会在网上传输许多冗余信息。比如,仅修改了某个Application的10个Paramater中的某1个的description属性,实际的修改可能仅有十几个字节的数据量,却要在网上传输整个对象的信息。
l 对数据库进行的密集的操作有可能会大大降低应用的吞吐能力。如果对象类型的属性比较多或对象嵌套层次比较深的话(就是说涉及到数据库里多张表),服务器在操作数据库时会锁住多张表,其它对相关表的访问请求(可能来自其它客户端或其它应用)被阻塞,导致效率大大降低。
l 在特定情况下,此方法可能会导致复杂的处理或干脆不可行,比如,如果应用中有别的地方引用了Parameter对象,数据库中一般会定义外键,此时,更新Parameter的属性是合理的,但对Parameter的删除操作会失败,从而使更新操作失败。
2.3 标记所有相关对象的增、删、改状态我们可以标记所有相关对象的当前状态,提供一种‘增量式的更新’方式。目前许多开发环境中提供的数据操作控件,一般都有batch模式,可以缓存对数据的操作,然后在真正提交时一次更新到数据库,以PowerBuilder的DataWindow为例,其内部有四个缓冲区(primary,deleted等),并且每行数据都相应的状态记录(new,modified等),我们也可以采用类似的方式。如以下伪代码:
class Parameter {
private String id;
private String name;
private String description;
private int status;//0:None, 1: New; 2: Modified, 3: Deleted etc.
//省略了getter和setter方法
……………………..
/* use this method to get XML format String which indicate update information of this object
@return a XML format String.
*/
public String getChangeInfo() {
}
}
//Application的类定义
class Application {
private String id;
private String name;
private String description;
private String definition;
private Parameter[] params
private Parameter[] deletedParams
private int status;
//省略了getter和setter方法
…………………
/* use this method to get XML format String which indicate update information of this object, here the information include information of subordinate classes.
@return a XML format String.
*/
public String getChangeInfo() {
……………………………….
}
}
//服务器上定义的方法
………………
public Application[] getApplications();
public void addApplication(Application app);
public void updateApplication(String changeInfo);
public void deleteApplication(Application app);
………………
使用此方法,对象的定义会复杂一些,因为对象要能够表明有那些下层对象被删掉啦,以上的例子中的对象定义仅为演示用,并不是最好的定义方式,比如,你也可以定义另外一个数组专门存放被修改过的Parameter等。
注意服务器上updateApplication方法的定义,此时,我们也可以传递的是一个XML格式的字符串,其中指明了对象的更新信息;其实,我们当然也可以传递一个Application对象,这样,我们也不会像第二种方法一样出现不能工作的情况,因为,服务器可以从对象中取得那些下层对象是应该更新的,那些是真正应该删除的。但如果仅仅修改了某一个下层对象的某一个简单属性的话,传递整个对象仍然会带来相当的冗余。比较好的方法是仅传入必须的修改信息,对于没有改动过的下层对象的信息,根本不再网络上传送,这里假设是使用XML来实现的。下面给出一个XML的例子:
<Application status = '2' id = '00001' name = 'appOne' description = '' definition = ''>
<ParamInfo>
<Deleted>
<param id = 'param009'/>
<param id = 'param005'/>
</Deleted>
<Modified>
<param id = 'param003' name = 'ParamThree' description = 'Changed'/>
</Modified>
<Added>
<param id = 'Server control' name = 'Other' description = ''/>
<param id = 'Server control' name = 'Another' description = ''/>
</Added>
</ParamInfo>
</Application>
这段XML片断表明,id为‘00001’的Application其基本信息被修改了,因为status为2,所以更新此Application应更新时应更新其基本信息,另外,此Application有两个Parameter对象(其id分别为‘param009’和‘param005’)被删除啦,用户修改了一个Parameter,并另加了两个新的Parameter。
注意到此Application的status所指明的状态是指的其基本信息,如果status为0并不意味着Application对象的下层对象也没改变。戏法人人会变,各有巧妙不同,这里标记信息的定义及XML的schema的定义都有很大的弹性。
2.3.1 优点l 保留了对象逻辑上的完整性
l 几乎没有冗余信息(这里用‘几乎’是因为XML标记本身是冗余信息,虽然数据量很小)
l 无须多余的数据库操作
l 如果对象定义的再复杂一些(比如定义一个Parameter的原始数据数组),用户甚至可以不用和服务器交互就回到对象原来的状态
l 灵活,弹性大,可扩展性比较强。
2.3.2 缺点l 实现起来会比较复杂,潜在的犯错误的可能也就比较大。
3. 结论好了,分析过了,是该做结论的时候了,我们的结论是——没有结论^_^ 所谓模式也是在特定情况下的对于特定问题的解决方法。已有了那么多经典模式,如果我只需要写一个‘Hello, World’程序的话,我还是会不使用模式,就是说,以上各种方法,我们应当根据手上问题的具体情况来决定具体选用那个。
一般来说,现在我们手上的工作,优先级比较高的是要短、平、快,我们需要尽快把东西搞出来,所以在条件允许的情况下,我们应该首选第二种方法。对于此方法的缺点,如果应用的规模大到这真的成了问题的程度的话,我们可以通过要求提高网络带宽及数据库服务器硬件的性能来克服^_^
第三种方法是最优雅的解决方法,如果你是完美主义者,或者想培养自己做科学家的品质的话,你可以优先选用此方法。对于我们普通俗人,只有在使用第二种方法不能接受的情况下才应该使用此方法。
第一种方法打破了对象本身的完整性,可能会惹恼‘上帝’,一般来说,不提倡使用,但某些应用模式下,有可能只能使用这种方式,而且用户也已对这种操作模式习以为常了,比如,用户界面是基于浏览器的瘦客户端,那么这时候也无需犹豫,就用这种方法好啦。
End of this document
[K1]压力要素
本文地址:http://com.8s8s.com/it/it37442.htm