该帖已经被评为良好帖
|
|
---|---|
作者 | 正文 |
发表时间:2008-01-23
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里添加配置项<mx:RemoteObject/> <!---->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
声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |
发表时间:2008-01-23
Cairngorm的确不能算一个好的MVC实现。其实我觉得它的方向就有问题,之所有有MXML,就是为了少写代码,尽可能用声明的方式来构造程序。Cairngorm这种把代码越撑越多的框架到底有什么好处?我们前一个项目用了Cairngorm,但是现在我非常后悔,很想把它扔掉,因为它除了把代码行数增加了几倍以外,事实上根本没起到方便维护的作用。
|
|
返回顶楼 | |
发表时间:2008-01-24
amf3由什么新特性吗,性能和吞吐有多少提高?
LZ研究过否? 当时,我用amf2时,3还是beta。 |
|
返回顶楼 | |
发表时间: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 它的反序列化的性能好像不是太好,但不好到什么程度倒是没作具体的研究,如果有朋友有研究数据的话,希望能分享一下 |
|
返回顶楼 | |
发表时间:2008-01-24
ltian 写道 看了最后的一个图,也就是你的UserVo采用lazy load方式获取DepartmentVO时候的代码,我觉得使用者使用这个类的时候可能并没有想象的那么美好。因为当DepartmentVO!=null时,调用方可以立即地,直接地得到这个对象。 而当DepartmentVO==null时,调用方还期望能够立即地、直接地得到这个对象,那恐怕要失望了。因为Flex访问后台服务的时候采用的是异步调用。
这一点我倒也曾经和你一样担心过这个问题,但Flex其实给我提供了一个data binding的功能,所以如果我们并不需要同步响应的时候,就让它异步好啦 当响应从Server端返回时,在它的result方法里改变页面component所binding的对象的值,Flex会帮你做自动的更新 |
|
返回顶楼 | |
发表时间: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/ |
|
返回顶楼 | |
发表时间:2008-01-24
能不能把写的实际的example放上来!
|
|
返回顶楼 | |
发表时间:2008-01-24
我现在用bds
蛮好的啊 其实就是fds的免费版 flex+bds的研究已经告一段落 现在在用flex+bds尝试写一个网游的demo |
|
返回顶楼 | |
发表时间:2008-01-24
airport 写道 能不能把写的实际的example放上来!
你对哪一个知识点感兴趣,我放上来和大家共享 或者后面有时间的话,我整理一下,放些代码上来 |
|
返回顶楼 | |
发表时间: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; } |
|
返回顶楼 | |