`
JavaJason
  • 浏览: 23400 次
  • 性别: Icon_minigender_1
  • 来自: 苏州
社区版块
存档分类
最新评论

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 namedepartment name

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

 

这个时候矛盾来了

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

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

 

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

<!---->2.  <!---->创建一个Command,这个Command要实现两个接口,ICommandIResponder

<!---->3.  <!---->创建一个FrontController来建立EventCommand的关连。

 

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

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里添加配置项<mx:RemoteObject/>

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

 

再来看看我们的写法吧:

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

2.调用方法

ServiceFactory.getService(ServiceFactory.USER_BIZ)

.callService(ServiceFactory.USER_BIZ_INSERT, [newVO], this.result);

 

<!---->四.   <!---->使用Cache Framework提升我们的性能

 

有空再做哦……

 

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

 

  • 描述: sequence diagram for command pattern
  • 大小: 20.3 KB
  • 描述: class diagram for command
  • 大小: 33.2 KB
  • 描述: class diagram for data model
  • 大小: 49.5 KB
分享到:
评论
19 楼 foxlee 2008-01-29  
ooxx@ooxx.ooxx

--管理员的分割线--
JavaEye禁止在论坛留邮箱,请用站内短信
18 楼 JavaJason 2008-01-29  
houwei 写道
Can u  upload  all your source code  . Thx


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

--管理员的分割线--
JavaEye禁止在论坛留邮箱,请用站内短信
17 楼 houwei 2008-01-29  
Can u  upload  all your source code  . Thx
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


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

我后面再研究研究这个问题,看看到底是哪里派发了四次这个事件
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
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!");
		}
	}
}
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
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

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




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;
}
9 楼 JavaJason 2008-01-24  
airport 写道
能不能把写的实际的example放上来!


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

或者后面有时间的话,我整理一下,放些代码上来
8 楼 vip01 2008-01-24  
我现在用bds
蛮好的啊
其实就是fds的免费版

flex+bds的研究已经告一段落
现在在用flex+bds尝试写一个网游的demo
7 楼 airport 2008-01-24  
能不能把写的实际的example放上来!
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/
5 楼 JavaJason 2008-01-24  
ltian 写道
看了最后的一个图,也就是你的UserVo采用lazy load方式获取DepartmentVO时候的代码,我觉得使用者使用这个类的时候可能并没有想象的那么美好。因为当DepartmentVO!=null时,调用方可以立即地,直接地得到这个对象。 而当DepartmentVO==null时,调用方还期望能够立即地、直接地得到这个对象,那恐怕要失望了。因为Flex访问后台服务的时候采用的是异步调用。


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

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

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

3 楼 galaxystar 2008-01-24  
amf3由什么新特性吗,性能和吞吐有多少提高?
LZ研究过否?

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

相关推荐

Global site tag (gtag.js) - Google Analytics