sycronize java,Flex + Java 中小型项目的代码构造研究

Flex + Java 中小型项目的代码结构研究

Flex Structure

前言

这两天写了一个研究Flex

+ Java的例子,供大家参考,这个例子主要是出于以下几点考虑的

1.系统性能和系统可维护性上的平衡(Value Object lazy load)

2.开发效率和代码可读性上的平衡(Command and CommandManager)

3.如何让Flex调用服务端的Service(AMF3,

Remote Object)

4.使用Cache Framework提升我们的性能

花絮:其实做项目和生活,管理等等都是一样,做到最好是不太现实的,但要和谐,什么叫和谐?就是在成本,进度,质量等外在压力下把代码写得最好!所以我下面的例子代码也是一样,追求的是一个平衡J

一.系统性能和系统可维护性上的平衡(Value Object lazy

load)

最佳性能时,系统只在网络上传输必要的数据,如显示用户清单时只传输user name和department name。

而结构最优时,传输的却是规范的数据结构。

这个时候矛盾来了

A.传输规范的数据结构。这时候必然会带上一些冗余数据,如显示用户清单时传输的UserVO,而UserVO里同时也包含了标志这个用户部门的DepartmentVO,这时就会带来不必要的数据传输,如果显示的用户清单有100条,那么这100个UserVO里面的DepartmentVO必然会带来不小的数据冗余。

B.只在网络上传输必要的数据。这时有两种方法可以做到,设计一个UserListVO,里面包含user name和department name这两样field,然后在Business

Logic里组装这个UserListVO。但这种方法显然有个大的缺点,这个VO或对应的业务逻辑代码不可以共用,因为不同的地方会有不同的业务需求,比如有一个模块中会要显示用户的年龄。另一个方法就是,使用规范的数据结构,但只为这些数据结构中必要的栏位设值,如上面所说的,可以只为userVO.departmentVO.name设值,但其它栏位保持null,显然,这个VO的共用性也不好,因为我没法知道这个VO里面的栏位是否已经被设值了。

综上所说,所以我取上面两种方法的一个中间点来解决这个问题(如下图),即使用完整的数据结构来存储数据,但不是必要的数据不会被加载上来,如果要用时,可以通过Lazy Load的方式加载。如UserVO里有DepartmentVO,但在显示清单时不需要user对应的department信息,在编辑时才需要,所以我们可以在popup出用户编辑窗口的时候才在UserVO的getDepartmentVO()方法中加载相应的DepartmentVO。

请参见附件中的class diagram for data model

二.开发效率和代码可读性上的平衡(Command and

CommandManager)

往往在开发的时候,标准的结构会多写很多代码,虽然结构很清晰,但老实说,对于我们的项目,好像不需要这样“清晰”,比如Cairngorm中有command, event, controller等等,这确实是一种清晰的结构,但写起来很麻烦,所以我下面设计了一种简化的结构来实现它(如下图)。

Class Diagram

请参见附件中的class diagram for command

Sequence Diagram

请参见附件中的sequence diagram for command pattern

关于Command

Pattern,请参考以下的链接

http://www.javaworld.com/javaworld/jw-06-2002/jw-0628-designpatterns.html

这里,CommandManager就是那个Invoker。而com.novem.farc.command.UserSaveCommand.datagrid就是那个receiver。

Why not

Cairngorm Event or Command?

我们以查找一个user为例,来看看Cairngorm是怎么调用一个Command并返回结果的。

1.创建一个CairngormEvent,并在这个Event里要有一个userId:Number的field。

2.创建一个Command,这个Command要实现两个接口,ICommand和IResponder。

3.创建一个FrontController来建立Event和Command的关连。

然后,在客户端调用的时候,书写如下的代码:

var event: EventFindUser = new EventFindUser

();

event.userId = userVO.id;

CairngormEventDispatcher.getInstance().dispatchEvent(

event );

我们现在新的结构是这样实现的:

var command:CommandFindUser = new

CommandFindUser();

command.userId = userVO.id;

NovemCommandManager.execute(command);

可以看出来,Cairngorm通过注册Event,并通过Event来传递输入参数,而我们自己的结构是将参数直接传递给Command,所以Cairngorm并没有给我们提供特别的方便,反而增加了不少麻烦的Event,而它提供的这种解耦,也并不实在。

Why not

Cairngorm Model Locator?

Cairngorm Model Locator提供的其实是一种静态全局变量。

那么,谁都可以来改变这个Model

Locator中的值,这显然是一个很危险的事。

如果大家也和我一样认为Cairngorm

Model Locator就是一种静态全局变量的话,我想我在这里不用说得太多,只要去查一下静态全局变量的好处坏处就可以了。

三.如何让Flex调用服务端的Service(AMF3, Remote Object)

暂且假定,我们的项目使用的Remote

Object方式去访问服务端

Why

not Cairngorm Delegate?

老规矩,我们先来看看Cairngorm是怎么来调用服务端的

1.在service.xml里添加配置项

2.创建Delegate.as,并为RemoteObject添加对应的方法(这里需要为每个服务端对象都创建对应的Delegate和方法,工作量不但不小,而且很烦哦)

再来看看我们的写法吧:

1.在ServiceFactory里添加需要调用的Service和method的名字常量

2.调用方法

ServiceFactory.getService(ServiceFactory.USER_BIZ)

.callService(ServiceFactory.USER_BIZ_INSERT,

[newVO], this.result);

四.使用Cache Framework提升我们的性能

有空再做哦……

但主要的思路是使用第三方的Cache工具在业务层做Cache

1 楼

treenode

2008-01-23

Cairngorm的确不能算一个好的MVC实现。其实我觉得它的方向就有问题,之所有有MXML,就是为了少写代码,尽可能用声明的方式来构造程序。Cairngorm这种把代码越撑越多的框架到底有什么好处?我们前一个项目用了Cairngorm,但是现在我非常后悔,很想把它扔掉,因为它除了把代码行数增加了几倍以外,事实上根本没起到方便维护的作用。

2 楼

1314520ln

2008-01-24

好文章,赞一个

3 楼

galaxystar

2008-01-24

amf3由什么新特性吗,性能和吞吐有多少提高?

LZ研究过否?

当时,我用amf2时,3还是beta。

4 楼

JavaJason

2008-01-24

galaxystar 写道

amf3由什么新特性吗,性能和吞吐有多少提高?

LZ研究过否?

当时,我用amf2时,3还是beta。

Remote Object是Flex与Server端交互性能最高的一种方法,同时也最符合我们的代码编写习惯,只是FDS费用不少,而GDS又不算太成熟,麻烦~~~

这是它新的Spec

http://download.macromedia.com/pub/labs/amf/amf3_spec_121207.pdf

它的反序列化的性能好像不是太好,但不好到什么程度倒是没作具体的研究,如果有朋友有研究数据的话,希望能分享一下

5 楼

JavaJason

2008-01-24

ltian 写道

看了最后的一个图,也就是你的UserVo采用lazy load方式获取DepartmentVO时候的代码,我觉得使用者使用这个类的时候可能并没有想象的那么美好。因为当DepartmentVO!=null时,调用方可以立即地,直接地得到这个对象。 而当DepartmentVO==null时,调用方还期望能够立即地、直接地得到这个对象,那恐怕要失望了。因为Flex访问后台服务的时候采用的是异步调用。

这一点我倒也曾经和你一样担心过这个问题,但Flex其实给我提供了一个data binding的功能,所以如果我们并不需要同步响应的时候,就让它异步好啦

当响应从Server端返回时,在它的result方法里改变页面component所binding的对象的值,Flex会帮你做自动的更新

6 楼

JavaJason

2008-01-24

希望这两篇文章对Remote Object感兴趣的朋友有用

More on RPC in Adobe Flex Applications with AMF, BlazeDS, and/or GraniteDS

http://www.infoq.com/news/2007/12/more-on-rpc-in-flex-with-amf;jsessionid=C357C8E63A2DE1D89B645B2616D45F31

BlazeBench: Why you want AMF and BlazeDS

http://www.jamesward.org/wordpress/2007/12/12/blazebench-why-you-want-amf-and-blazeds/

7 楼

airport

2008-01-24

能不能把写的实际的example放上来!

8 楼

vip01

2008-01-24

我现在用bds

蛮好的啊

其实就是fds的免费版

flex+bds的研究已经告一段落

现在在用flex+bds尝试写一个网游的demo

9 楼

JavaJason

2008-01-24

airport 写道

能不能把写的实际的example放上来!

你对哪一个知识点感兴趣,我放上来和大家共享

或者后面有时间的话,我整理一下,放些代码上来

10 楼

JavaJason

2008-01-24

ltian 写道

JavaJason 写道

ltian 写道

看了最后的一个图,也就是你的UserVo采用lazy load方式获取DepartmentVO时候的代码,我觉得使用者使用这个类的时候可能并没有想象的那么美好。因为当DepartmentVO!=null时,调用方可以立即地,直接地得到这个对象。 而当DepartmentVO==null时,调用方还期望能够立即地、直接地得到这个对象,那恐怕要失望了。因为Flex访问后台服务的时候采用的是异步调用。

这一点我倒也曾经和你一样担心过这个问题,但Flex其实给我提供了一个data binding的功能,所以如果我们并不需要同步响应的时候,就让它异步好啦

当响应从Server端返回时,在它的result方法里改变页面component所binding的对象的值,Flex会帮你做自动的更新

Flex给我提供的data binding的功能确实好用,但是按照上面的实现,我们所提供的getXXXX方法API的语义就不稳定了,再一定程度上会造成使用者的混乱,可能需要加强团度的培训,以使所有成员都必须清楚地认识到这一点。

你这一点算是说到我心里去了

这也是我遇到的一个问题之一,也还没有再做深入的研究,有这方面的经验分享一下吗?

这是我的UserVO.as中的代码

[Bindable]

public var departmentVO : DepartmentVO;

//todo

//why cannot use "get departmentVO()", and bind userVO.departmentVO.name to datagrid

public function getDepartmentVO():DepartmentVO

{

var responder:LazyLoaderResponder;

if(departmentVO == null && !isNaN(departmentId))

{

responder = new LazyLoaderResponder(this, "departmentVO");

ServiceFactory.getService(ServiceFactory.DEPARTMENT_BIZ).callServiceWithResponder(ServiceFactory.DEPARTMENT_BIZ_LOAD, [departmentId], responder);

}

return departmentVO;

}

11 楼

houwei

2008-01-25

ltian 写道

JavaJason 写道

ltian 写道

JavaJason 写道

ltian 写道

看了最后的一个图,也就是你的UserVo采用lazy load方式获取DepartmentVO时候的代码,我觉得使用者使用这个类的时候可能并没有想象的那么美好。因为当DepartmentVO!=null时,调用方可以立即地,直接地得到这个对象。 而当DepartmentVO==null时,调用方还期望能够立即地、直接地得到这个对象,那恐怕要失望了。因为Flex访问后台服务的时候采用的是异步调用。

这一点我倒也曾经和你一样担心过这个问题,但Flex其实给我提供了一个data binding的功能,所以如果我们并不需要同步响应的时候,就让它异步好啦

当响应从Server端返回时,在它的result方法里改变页面component所binding的对象的值,Flex会帮你做自动的更新

Flex给我提供的data binding的功能确实好用,但是按照上面的实现,我们所提供的getXXXX方法API的语义就不稳定了,再一定程度上会造成使用者的混乱,可能需要加强团度的培训,以使所有成员都必须清楚地认识到这一点。

你这一点算是说到我心里去了

这也是我遇到的一个问题之一,也还没有再做深入的研究,有这方面的经验分享一下吗?

这是我的UserVO.as中的代码

[Bindable]

public var departmentVO : DepartmentVO;

//todo

//why cannot use "get departmentVO()", and bind userVO.departmentVO.name to datagrid

public function getDepartmentVO():DepartmentVO

{

var responder:LazyLoaderResponder;

if(departmentVO == null && !isNaN(departmentId))

{

responder = new LazyLoaderResponder(this, "departmentVO");

ServiceFactory.getService(ServiceFactory.DEPARTMENT_BIZ).callServiceWithResponder(ServiceFactory.DEPARTMENT_BIZ_LOAD, [departmentId], responder);

}

return departmentVO;

}

首先感谢你分享思路,说实话,我也刚开始这方面的工作,还没有深入研究,如果有好的实践一定与大家分享。

It is impossible to  use

"function getDepartmentVO():DepartmentVO  "

to get  the return  DepartmentVO

Because you use  Remote Object!

Generally  the Sychronize way you can use

function getDepartmentVO():DepartmentVO  {

dosmothing;

return DepartmentVO ;

}

But  ASycronize way you cant do this

it should be :

[Bindable]

var returnVO:DepartmentVO;

function getDepartmentVO():void{

myservice.getVOByRemoterObject();

the RemoteObject.addListener("getDepartment", theDepartmentVOHandler);

}

function theDepartmentVOHandler(event Resultevent){

returnVO = event.casttoDepartmentVO;

}

it is  impossible to  getDepartmentVO() directly

12 楼

JavaJason

2008-01-25

houwei 写道

ltian 写道

JavaJason 写道

ltian 写道

JavaJason 写道

ltian 写道

看了最后的一个图,也就是你的UserVo采用lazy load方式获取DepartmentVO时候的代码,我觉得使用者使用这个类的时候可能并没有想象的那么美好。因为当DepartmentVO!=null时,调用方可以立即地,直接地得到这个对象。 而当DepartmentVO==null时,调用方还期望能够立即地、直接地得到这个对象,那恐怕要失望了。因为Flex访问后台服务的时候采用的是异步调用。

这一点我倒也曾经和你一样担心过这个问题,但Flex其实给我提供了一个data binding的功能,所以如果我们并不需要同步响应的时候,就让它异步好啦

当响应从Server端返回时,在它的result方法里改变页面component所binding的对象的值,Flex会帮你做自动的更新

Flex给我提供的data binding的功能确实好用,但是按照上面的实现,我们所提供的getXXXX方法API的语义就不稳定了,再一定程度上会造成使用者的混乱,可能需要加强团度的培训,以使所有成员都必须清楚地认识到这一点。

你这一点算是说到我心里去了

这也是我遇到的一个问题之一,也还没有再做深入的研究,有这方面的经验分享一下吗?

这是我的UserVO.as中的代码

[Bindable]

public var departmentVO : DepartmentVO;

//todo

//why cannot use "get departmentVO()", and bind userVO.departmentVO.name to datagrid

public function getDepartmentVO():DepartmentVO

{

var responder:LazyLoaderResponder;

if(departmentVO == null && !isNaN(departmentId))

{

responder = new LazyLoaderResponder(this, "departmentVO");

ServiceFactory.getService(ServiceFactory.DEPARTMENT_BIZ).callServiceWithResponder(ServiceFactory.DEPARTMENT_BIZ_LOAD, [departmentId], responder);

}

return departmentVO;

}

首先感谢你分享思路,说实话,我也刚开始这方面的工作,还没有深入研究,如果有好的实践一定与大家分享。

It is impossible to  use

"function getDepartmentVO():DepartmentVO  "

to get  the return  DepartmentVO

Because you use  Remote Object!

Generally  the Sychronize way you can use

function getDepartmentVO():DepartmentVO  {

dosmothing;

return DepartmentVO ;

}

But  ASycronize way you cant do this

it should be :

[Bindable]

var returnVO:DepartmentVO;

function getDepartmentVO():void{

myservice.getVOByRemoterObject();

the RemoteObject.addListener("getDepartment", theDepartmentVOHandler);

}

function theDepartmentVOHandler(event Resultevent){

returnVO = event.casttoDepartmentVO;

}

it is  impossible to  getDepartmentVO() directly

Please take a look at following two lines

responder = new LazyLoaderResponder(this, "departmentVO");

ServiceFactory.getService(ServiceFactory.DEPARTMENT_BIZ).callServiceWithResponder(ServiceFactory.DEPARTMENT_BIZ_LOAD, [departmentId], responder);

LazyLoaderResponder is the event handler

It's the same solution you provide

13 楼

houwei

2008-01-25

public function getDepartmentVO():DepartmentVO

{

var responder:LazyLoaderResponder;

if(departmentVO == null && !isNaN(departmentId))

{

responder = new LazyLoaderResponder(this, "departmentVO");

ServiceFactory.getService(ServiceFactory.DEPARTMENT_BIZ).callServiceWithResponder(ServiceFactory.DEPARTMENT_BIZ_LOAD, [departmentId], responder);

}

return departmentVO;

}

But you still have a  "return departmentVO"  to Return a object  from your function.  This is  null object if you  call this method.

it is impossible to RETURN departmentVO  in  Asyc function.

That is my point.

anyway  your LazyLoaderResponder looks clean

Could you  post your  LazyLoaderResponder ?

Thanks

14 楼

JavaJason

2008-01-28

houwei 写道

public function getDepartmentVO():DepartmentVO

{

var responder:LazyLoaderResponder;

if(departmentVO == null && !isNaN(departmentId))

{

responder = new LazyLoaderResponder(this, "departmentVO");

ServiceFactory.getService(ServiceFactory.DEPARTMENT_BIZ).callServiceWithResponder(ServiceFactory.DEPARTMENT_BIZ_LOAD, [departmentId], responder);

}

return departmentVO;

}

But you still have a  "return departmentVO"  to Return a object  from your function.  This is  null object if you  call this method.

it is impossible to RETURN departmentVO  in  Asyc function.

That is my point.

anyway  your LazyLoaderResponder looks clean

Could you  post your  LazyLoaderResponder ?

Thanks

这个问题问得非常好,我也确实遇到这个问题,但经过测试后,没有发现问题

我在server端的load department方法里加了一个Thread.sleep(10000); 测下来没有问题

另外,我有一个疑惑还没有来得及研究,那就是,如果我的datagrid只有一条记录,为什么我的label function会被调用4次,两次departmentVO为null,两次是有值的

LazyLoaderResponder的代码

package com.xxxxx.common.responder

{

import mx.controls.Alert;

import mx.rpc.IResponder;

public class LazyLoaderResponder implements IResponder

{

private var _target : Object;

private var _fieldName : String;

public function LazyLoaderResponder(target:Object, fieldName:String)

{

_target = target;

_fieldName = fieldName;

}

public function result( data : Object ) : void

{

_target[_fieldName] = data;

}

public function fault( info : Object ) : void

{

Alert.show("Lazy load "+_target+"."+_fieldName+" failed!");

}

}

}

15 楼

houwei

2008-01-29

JavaJason 写道

houwei 写道

public function getDepartmentVO():DepartmentVO

{

var responder:LazyLoaderResponder;

if(departmentVO == null && !isNaN(departmentId))

{

responder = new LazyLoaderResponder(this, "departmentVO");

ServiceFactory.getService(ServiceFactory.DEPARTMENT_BIZ).callServiceWithResponder(ServiceFactory.DEPARTMENT_BIZ_LOAD, [departmentId], responder);

}

return departmentVO;

}

But you still have a  "return departmentVO"  to Return a object  from your function.  This is  null object if you  call this method.

it is impossible to RETURN departmentVO  in  Asyc function.

That is my point.

anyway  your LazyLoaderResponder looks clean

Could you  post your  LazyLoaderResponder ?

Thanks

这个问题问得非常好,我也确实遇到这个问题,但经过测试后,没有发现问题

我在server端的load department方法里加了一个Thread.sleep(10000); 测下来没有问题

另外,我有一个疑惑还没有来得及研究,那就是,如果我的datagrid只有一条记录,为什么我的label function会被调用4次,两次departmentVO为null,两次是有值的

LazyLoaderResponder的代码

package com.xxxxx.common.responder

{

import mx.controls.Alert;

import mx.rpc.IResponder;

public class LazyLoaderResponder implements IResponder

{

private var _target : Object;

private var _fieldName : String;

public function LazyLoaderResponder(target:Object, fieldName:String)

{

_target = target;

_fieldName = fieldName;

}

public function result( data : Object ) : void

{

_target[_fieldName] = data;

}

public function fault( info : Object ) : void

{

Alert.show("Lazy load "+_target+"."+_fieldName+" failed!");

}

}

}

I guess  it is ur datgrid has more than 1 column, u can try to  cut ur column to  see  if u get less call for  ur lable function

icon_smile.gif

16 楼

JavaJason

2008-01-29

houwei 写道

JavaJason 写道

houwei 写道

public function getDepartmentVO():DepartmentVO

{

var responder:LazyLoaderResponder;

if(departmentVO == null && !isNaN(departmentId))

{

responder = new LazyLoaderResponder(this, "departmentVO");

ServiceFactory.getService(ServiceFactory.DEPARTMENT_BIZ).callServiceWithResponder(ServiceFactory.DEPARTMENT_BIZ_LOAD, [departmentId], responder);

}

return departmentVO;

}

But you still have a  "return departmentVO"  to Return a object  from your function.  This is  null object if you  call this method.

it is impossible to RETURN departmentVO  in  Asyc function.

That is my point.

anyway  your LazyLoaderResponder looks clean

Could you  post your  LazyLoaderResponder ?

Thanks

这个问题问得非常好,我也确实遇到这个问题,但经过测试后,没有发现问题

我在server端的load department方法里加了一个Thread.sleep(10000); 测下来没有问题

另外,我有一个疑惑还没有来得及研究,那就是,如果我的datagrid只有一条记录,为什么我的label function会被调用4次,两次departmentVO为null,两次是有值的

LazyLoaderResponder的代码

package com.xxxxx.common.responder

{

import mx.controls.Alert;

import mx.rpc.IResponder;

public class LazyLoaderResponder implements IResponder

{

private var _target : Object;

private var _fieldName : String;

public function LazyLoaderResponder(target:Object, fieldName:String)

{

_target = target;

_fieldName = fieldName;

}

public function result( data : Object ) : void

{

_target[_fieldName] = data;

}

public function fault( info : Object ) : void

{

Alert.show("Lazy load "+_target+"."+_fieldName+" failed!");

}

}

}

I guess  it is ur datgrid has more than 1 column, u can try to  cut ur column to  see  if u get less call for  ur lable function

icon_smile.gif

仍然有4次调用的,并且不幸的是,有两次都会去server端拿值,这个比较麻烦

我后面再研究研究这个问题,看看到底是哪里派发了四次这个事件

17 楼

houwei

2008-01-29

Can u  upload  all your source code  . Thx

18 楼

JavaJason

2008-01-29

houwei 写道

Can u  upload  all your source code  . Thx

太大了,我发邮件给你,告诉我,你的邮箱吧

--管理员的分割线--

JavaEye禁止在论坛留邮箱,请用站内短信

19 楼

foxlee

2008-01-29

ooxx@ooxx.ooxx

--管理员的分割线--

JavaEye禁止在论坛留邮箱,请用站内短信

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值