分布式环境下对具有对象类型的属性的对象的添加、删除及修改

类别:软件工程 点击:0 评论:0 推荐:

 

分布式环境下对具有对象类型的属性的对象的添加、删除及修改

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