- 浏览: 269683 次
- 性别:
- 来自: 深圳
文章分类
最新评论
-
laitaogood:
请问,还有哪些API服务呢?比如说查看用户问答积分啊,用户发帖 ...
JavaEye-Api -
ruixinzheng:
闲聊一下
URL: http://api.iteye.com ...
JavaEye-Api -
ling520:
.....
思绪起*心间 -
ling520:
亲爱的,对不起哦~以后我一定尽量不让你等了~
今天,突然有了一种失落感 -
ling520:
小兔子想你了哈~
我很想你
pureMVC是一个MVC框架,皆在最大限度的减少MVC间的耦合性。本人刚刚接触pureMVC时感到一头雾水,不知从何入手,也不知道从何学习。好在本人有耐性且能看懂英文技术文档,面向对象的编程能力也比较扎实。在这种背景下,终于悟出了pureMVC的原理,能够使用pureMVC进行开发。如果把pureMVC的领悟境界分为若干,我是处于最低境界(潜力很大啊)。好,闲话不说,言归正传。
本示例是在理解了官方示例EmployeeAdmin示例的基础之上的简化版,用意是为了更好的理解pureMVC。
界面:图片上部控件用于添加用户,下部控件用户显示用户信息和删除已添加的用户
【图1】
首先:
pureMVC既然是MVC(Model、View、Controller)框架,那你就必须要记住pureMVC中的四个主要结构类:
- Proxy(相当于Model)
- Mediator(相当于View)
- Command(相当于Controller)
- Facade(管理Proxy、Mediator和Command的东西)
也许上面小括号中的话说的不严谨,在本示例中MVC是这样划分的:
- Model:由VO(Value Object,即值对象)和Proxy组成;
- Mediator:由图1中的两个MXML文件及其对应的Mediator组成;
- Command:由一个MacroCommand和若干个SimpleCommand组成;
- Facade:即ApplicationFacade;
- 启动页:MyPureMVCDemo.mxml文件。
整个示例由以上五个部分组成。具体如图2所示。
【图2】
这里关于3和4要做一下解释。pureMVC中的Command分为两种:多命令和单一命令,即MacroCommand和SimpleCommand。MacroCommand中通过addSubCommand(SimpleCommandName)来加入子命令,两个Command中的方法都需要被重写override,此外还需要通过facade.registerCommand(...)注册命令。也许这段话我说的不清楚,你只要记住pureMVC框架包含这五个部分就可以了,咱们往下看吧!
基于PureMVC的一个Flex MP3播放器分析
做Flex做久了做大了,就会觉得之前写的的Flex代码开始有点乱,哪怕你写的规范了,但总觉得结构松散,维护不方便,相信很多人刚开始做Flex的时候,都是想到什么功能,就写什么功能,或者有些好点的,就先画了个大体的流程图之类的,因为现在Flex普及得还不够,很多人做Flex也是试探阶段,不敢用作商业项目或其它大项目,只会用来试水技术层面的,所以都是做些小应用的多,就会忽略了设计一个比较好的框架来开发。所以Flex的开发框架就应运而生了。目前,好的Flex开发框架还不多,官方有个Cairngorm的框架,可能有些人会说这个框架有点复杂,其实不然,对比起Ruby的Rails,Java的Struts,Spring之类的开发框架,就显得简单得多了。只要清楚了解要MVC的概念,就会对这些框架并不陌生,但是今天的主角不是Cairngorm,而是另一个Flex框架 PureMVC,如果说Cairngorm复杂的话,那么PureMVC就显得简单多了,PureMVC比较轻盈,核心也只有十来个类,是一个轻量级的Flex框架,但PureMVC的通用性还是比较广的,有PHP的,有Java的有Python的。可能直接说框架的使用会比较抽象,那么就由一个实例来开始讲解吧,就用一个PureMVC做的一个MP3播放器。
先来看看PureMVC的结构图:
在图中,Facade,Model,View,Controller都是PureMVC的四个核心类,都是单例模式的,用户无需操作那Model,View,Controller类,而用户只需要操作Facade就够了,Facade类用来管理其它的三个单例类,顾名思义,那三个类都是分别对应 MVC 模式的那三个元素,Facade也是个单例,它负责创建,激活,调用其它的三个类,管理MVC各屋的生命周期。
而我们看看Model类,又细分了一个Proxy类出来,我们称其为代理吧,就是对数据模型的一个代理,负责访问我们的数据对象(Data Object)也就是Cairngorm中的ValueObject,其实都是同一个概念。而类结构上,对数据操作的代理Proxy类就只有一个,但可以从我们的应用上又分为Local Proxy,Remote Proxy,其实都只是Proxy ,只是根据用户的应用的不同,在Proxy里面实现不同的功能而已,比如如果你操作本地数据(内存中的数据,并非本地操作系统的文件),你可以写一些VO的getter/setter直接操作数据,而如果是Remote的数据的话,你可以在Proxy类里定义一些HttpService,URLLoader,WebService等等的访问远程数据的API,之后将获取到的远程数据放在VO中。
在Controller类里分出一个叫Command的类来,直接翻译的话,就是“命令”类,通常这些类都是用来处理一些业务流程,某些算法操作等等的操作。比如现在用户单击了“获取数据”的按钮,程序将从Proxy类里访问服务器,服务器返回数据之后,那些都是程序看得懂的数据,比如是XML,而如果数据结构比较复杂,你不可能直接将数据显示给用户看吧?那就将解析这些数据的工作交给Command来做,比如写一个ParseCommand的类,将获得的XML数据传递给该Command,在Command里进行数据的过滤,排列,整理等等的功能。再将组积好后数据交给Mediator来进行显示,而Mediator,就是下面我们要说的。
在View类里分出一个Mediator的类,该类是用来对ViewComponent操作的,我们暂且叫它“中介类”吧,为什么叫“中介”呢?其实就是用户界面(UI)与程序结构逻辑之间的中介,因为用户在界面上的操作,比如Button的Click事件不是直接反映到Command或者Proxy类上的,而是反映给Mediator类,在Mediator里作一些简单处理,比如验证合法性,触发其它ViewComponent的状态等,在这里也会将用户的数据封装在VO里面,再交由Command或Proxy来进一步处理。基本上Mediator只对用户的操作或用户提交的数据进行封装并简单预处理,而业务逻辑,存储服务的就应交给Command和Proxy来做,这样MVC分工好,使得程序结构比较严紧,可读性强,实现松耦合。当你改变了UI时,只需要对Mediator进行相应的改变就行了,而你改变了业务的逻辑与算法之类的话,也相应的改变Command就可以了,对其它模块的影响不大。
在上面这个图中,没有列出来的一个很重的类,就是 Notification 类,这个类为什么十分重要,可以说也是PureMVC的润滑剂,因为他是连接MVC各大部分的一个消息机制,就像是Cairngome里面的CairngomeEvent与FrontController,为了实现更好的松耦合,就是靠这个消息机制,因为各大部分中,很少直接的引用调用,而是以“发消息”(或者说是通知吧)来相互数据交流与通讯,这里是很好的使用了“观察者模式”,因此,在某一部分改变的处理逻辑的话,只是它所发送的消息没有改变,或者所侦听的消息没有改变,那么就不会影响到其它部分。
另外要注意几点,Command类是短生命周期的,也就是说,当有消息通知需要用到该Command进行处理时,Facade就会创建这个Command类,并将数据传入Command里面进行处理,当处理完成后,其生命周期就会结束,所以不要将一些长生命周期的数据存放在Command里,比如不要将一些状态数据信息存放在Command里面。还有就是Proxy类只会发送“消息”(通知),而不会接收任何消息,而Mediator与Command则可以发送与接收,所以你不能直接发消息通知Proxy去加载数据,而是通过引用Proxy的实例调用相关的函数。理论就说了一大堆了,我们来看看那个MP3播放器实例吧!
我们先来看看主程序的代码,PureMVC的入口点:
先来看看PureMVC的结构图:
在图中,Facade,Model,View,Controller都是PureMVC的四个核心类,都是单例模式的,用户无需操作那Model,View,Controller类,而用户只需要操作Facade就够了,Facade类用来管理其它的三个单例类,顾名思义,那三个类都是分别对应 MVC 模式的那三个元素,Facade也是个单例,它负责创建,激活,调用其它的三个类,管理MVC各屋的生命周期。
而我们看看Model类,又细分了一个Proxy类出来,我们称其为代理吧,就是对数据模型的一个代理,负责访问我们的数据对象(Data Object)也就是Cairngorm中的ValueObject,其实都是同一个概念。而类结构上,对数据操作的代理Proxy类就只有一个,但可以从我们的应用上又分为Local Proxy,Remote Proxy,其实都只是Proxy ,只是根据用户的应用的不同,在Proxy里面实现不同的功能而已,比如如果你操作本地数据(内存中的数据,并非本地操作系统的文件),你可以写一些VO的getter/setter直接操作数据,而如果是Remote的数据的话,你可以在Proxy类里定义一些HttpService,URLLoader,WebService等等的访问远程数据的API,之后将获取到的远程数据放在VO中。
在Controller类里分出一个叫Command的类来,直接翻译的话,就是“命令”类,通常这些类都是用来处理一些业务流程,某些算法操作等等的操作。比如现在用户单击了“获取数据”的按钮,程序将从Proxy类里访问服务器,服务器返回数据之后,那些都是程序看得懂的数据,比如是XML,而如果数据结构比较复杂,你不可能直接将数据显示给用户看吧?那就将解析这些数据的工作交给Command来做,比如写一个ParseCommand的类,将获得的XML数据传递给该Command,在Command里进行数据的过滤,排列,整理等等的功能。再将组积好后数据交给Mediator来进行显示,而Mediator,就是下面我们要说的。
在View类里分出一个Mediator的类,该类是用来对ViewComponent操作的,我们暂且叫它“中介类”吧,为什么叫“中介”呢?其实就是用户界面(UI)与程序结构逻辑之间的中介,因为用户在界面上的操作,比如Button的Click事件不是直接反映到Command或者Proxy类上的,而是反映给Mediator类,在Mediator里作一些简单处理,比如验证合法性,触发其它ViewComponent的状态等,在这里也会将用户的数据封装在VO里面,再交由Command或Proxy来进一步处理。基本上Mediator只对用户的操作或用户提交的数据进行封装并简单预处理,而业务逻辑,存储服务的就应交给Command和Proxy来做,这样MVC分工好,使得程序结构比较严紧,可读性强,实现松耦合。当你改变了UI时,只需要对Mediator进行相应的改变就行了,而你改变了业务的逻辑与算法之类的话,也相应的改变Command就可以了,对其它模块的影响不大。
在上面这个图中,没有列出来的一个很重的类,就是 Notification 类,这个类为什么十分重要,可以说也是PureMVC的润滑剂,因为他是连接MVC各大部分的一个消息机制,就像是Cairngome里面的CairngomeEvent与FrontController,为了实现更好的松耦合,就是靠这个消息机制,因为各大部分中,很少直接的引用调用,而是以“发消息”(或者说是通知吧)来相互数据交流与通讯,这里是很好的使用了“观察者模式”,因此,在某一部分改变的处理逻辑的话,只是它所发送的消息没有改变,或者所侦听的消息没有改变,那么就不会影响到其它部分。
另外要注意几点,Command类是短生命周期的,也就是说,当有消息通知需要用到该Command进行处理时,Facade就会创建这个Command类,并将数据传入Command里面进行处理,当处理完成后,其生命周期就会结束,所以不要将一些长生命周期的数据存放在Command里,比如不要将一些状态数据信息存放在Command里面。还有就是Proxy类只会发送“消息”(通知),而不会接收任何消息,而Mediator与Command则可以发送与接收,所以你不能直接发消息通知Proxy去加载数据,而是通过引用Proxy的实例调用相关的函数。理论就说了一大堆了,我们来看看那个MP3播放器实例吧!
我们先来看看主程序的代码,PureMVC的入口点:
1 <?xml version="1.0" encoding="utf-8"?>
2 <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
3 xmlns:view="com.jiangzone.flex.pureplayer.view.ui.*"
4 verticalGap="2"
5 layout="vertical" creationComplete="fadace.startup(this)"
6 backgroundColor="0x444444">
7 <mx:Style>
8 //这里的CSS代码略去
9 </mx:Style>
10
11 <mx:Script>
12 <![CDATA[
13 import com.jiangzone.flex.pureplayer.ApplicationFacade;
14
15 private var fadace:ApplicationFacade = ApplicationFacade.getInstance();
16
17 ]]>
18 </mx:Script>
19 <mx:Box width="131">
20 <view:ProgressBoard id="progressBoard" />
21 <view:ControlBoard id="controlBoard" />
22 <view:SongListBoard id="songListBoard" />
23 </mx:Box>
24
25 </mx:Application>
2 <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
3 xmlns:view="com.jiangzone.flex.pureplayer.view.ui.*"
4 verticalGap="2"
5 layout="vertical" creationComplete="fadace.startup(this)"
6 backgroundColor="0x444444">
7 <mx:Style>
8 //这里的CSS代码略去
9 </mx:Style>
10
11 <mx:Script>
12 <![CDATA[
13 import com.jiangzone.flex.pureplayer.ApplicationFacade;
14
15 private var fadace:ApplicationFacade = ApplicationFacade.getInstance();
16
17 ]]>
18 </mx:Script>
19 <mx:Box width="131">
20 <view:ProgressBoard id="progressBoard" />
21 <view:ControlBoard id="controlBoard" />
22 <view:SongListBoard id="songListBoard" />
23 </mx:Box>
24
25 </mx:Application>
从上面代码我们看到,定义了一个 facade 这个就是Facade的一个实例,而ApplicationFacade是继承自Facade类的,这个就是PureMVC的整个架构的控制管理类,因为Facade是一个单例,所以不能直接new 的,所以在ApplicationFacade里面定义了一个静态方法来获取它的实例。在程序的createComplete事实触发的时候,我们就调用facade.startup(this)这个方法,意思就是启动整个框架。
这里的代码都比较简单,我们再来看看ApplicationFacade的代码:
1 package com.jiangzone.flex.pureplayer
2 {
3 import org.puremvc.as3.interfaces.IFacade;
4 import org.puremvc.as3.patterns.facade.Facade;
5 import org.puremvc.as3.patterns.observer.Notification;
6
7 import com.jiangzone.flex.pureplayer.controller.StartupCommand;
8
9 public class ApplicationFacade extends Facade implements IFacade
10 {
11 // Notification name constants
12 public static const STARTUP:String = "startup";
13
14
15 /**
16 * Singleton ApplicationFacade Factory Method
17 */
18 public static function getInstance() : ApplicationFacade {
19 if ( instance == null ) instance = new ApplicationFacade( );
20 return instance as ApplicationFacade;
21 }
22
23 /**
24 * Start the application
25 */
26 public function startup(app:Object):void
27 {
28 sendNotification( STARTUP, app );
29 }
30
31 /**
32 * Register Commands with the Controller
33 */
34 override protected function initializeController( ) : void
35 {
36 super.initializeController();
37 registerCommand( STARTUP, StartupCommand );
38 }
39
40 }
41 }
2 {
3 import org.puremvc.as3.interfaces.IFacade;
4 import org.puremvc.as3.patterns.facade.Facade;
5 import org.puremvc.as3.patterns.observer.Notification;
6
7 import com.jiangzone.flex.pureplayer.controller.StartupCommand;
8
9 public class ApplicationFacade extends Facade implements IFacade
10 {
11 // Notification name constants
12 public static const STARTUP:String = "startup";
13
14
15 /**
16 * Singleton ApplicationFacade Factory Method
17 */
18 public static function getInstance() : ApplicationFacade {
19 if ( instance == null ) instance = new ApplicationFacade( );
20 return instance as ApplicationFacade;
21 }
22
23 /**
24 * Start the application
25 */
26 public function startup(app:Object):void
27 {
28 sendNotification( STARTUP, app );
29 }
30
31 /**
32 * Register Commands with the Controller
33 */
34 override protected function initializeController( ) : void
35 {
36 super.initializeController();
37 registerCommand( STARTUP, StartupCommand );
38 }
39
40 }
41 }
这里分析一下,在ApplicationFacade类里,我们定义了一个String的常量,这个只是一个消息的类型,跟Flex里的Event的常量一样的,注意,规范化一点的话,应该将消息类型的字符串都定义为常量,而我在后面的代码中为了省事就直接用“XXXXX”这样的字串代替了,还是建义写成静态常量。
我们看到了startup()的代码了,就是在刚才主程序里调用的那个函数,这里接收了一个主程序的引用。
我们还看到了有一个initializeController( ) 的函数,当这个ApplicationFacade被实例化加载的时候,会先自动调用initializeController( ) 这个函数,所以,我们应当在ApplicationFacade在被初始化的时候,就对Command进行注册,说就是注册,其实也只是将一个Command与一个消息绑定在一起而已。当发送该消息时,Facade就会自动的找到那个Command并实例化执行。
registerCommand( STARTUP, StartupCommand );这句就是对Command进行注册的代码,registerCommand都是父类或接口里面定义的方法,我们先不用管它,STARTUP就是上面定义的一个常量,表示一个消息的类型,StartupCommand这个类就是我定义的一个Command类,这里说白了就是STARTUP这个字符串常量就是一个Key,而StartupCommand就是一个Value,存放在一个数组里面,当有人发送一个STARTUP的消息时,程序就自动生成一个StartupCommand来处理。
我们再看看startup()这个方法,在刚才的主程序里调用这个方法时,传入了一个传入了一个参数this,就是主程序本身,在startup()方法里面,发送了一个消息 sendNotification( STARTUP, app ); sendNotification()这个是发送消息的方法,第一个参数是消息的类型,第二个是可选参数,是消息的内容(消息体),在这里,将主程序的引用作为消息体绑在消息里一起发送了。由于之前在初始化的时候将STARTUP消息类型与StartupCommand绑定在一起了,所以当发送这个消息的时候,StartupCommand将会被通知,所以这时候,程序的流程就跳入到StartupCommand类里面。下面我们来看StartupCommand类的内容:
1 package com.jiangzone.flex.pureplayer.controller
2 {
3 import org.puremvc.as3.interfaces.ICommand;
4 //这里略去一些import代码
5 import com.jiangzone.flex.pureplayer.model.PlayListProxy;
6
7 public class StartupCommand extends SimpleCommand implements ICommand
8 {
9 override public function execute( note:INotification ) : void
10 {
11 /**
12 * 获取消息体内容,在发送STARTUP消息时,将主程序PurePlayer作为消息体跟随消息传送
13 */
14 var app:PurePlayer = note.getBody() as PurePlayer;
15
16 /**
17 * 注册代理(Model)
18 */
19 facade.registerProxy(new SongProxy());
20 facade.registerProxy(new PlayListProxy());
21 /**
22 * 注册ViewComponents或者UI组件的中介器。
23 */
24 facade.registerMediator(new ControlBoardMediator(app.controlBoard));
25 facade.registerMediator( new SongListBoardMediator(app.songListBoard));
26 facade.registerMediator(new ProgressBoardMediator(app.progressBoard));
27
28 (facade.retrieveProxy(PlayListProxy.NAME) as PlayListProxy).loadPlayList();
29 }
30 }
31 }
2 {
3 import org.puremvc.as3.interfaces.ICommand;
4 //这里略去一些import代码
5 import com.jiangzone.flex.pureplayer.model.PlayListProxy;
6
7 public class StartupCommand extends SimpleCommand implements ICommand
8 {
9 override public function execute( note:INotification ) : void
10 {
11 /**
12 * 获取消息体内容,在发送STARTUP消息时,将主程序PurePlayer作为消息体跟随消息传送
13 */
14 var app:PurePlayer = note.getBody() as PurePlayer;
15
16 /**
17 * 注册代理(Model)
18 */
19 facade.registerProxy(new SongProxy());
20 facade.registerProxy(new PlayListProxy());
21 /**
22 * 注册ViewComponents或者UI组件的中介器。
23 */
24 facade.registerMediator(new ControlBoardMediator(app.controlBoard));
25 facade.registerMediator( new SongListBoardMediator(app.songListBoard));
26 facade.registerMediator(new ProgressBoardMediator(app.progressBoard));
27
28 (facade.retrieveProxy(PlayListProxy.NAME) as PlayListProxy).loadPlayList();
29 }
30 }
31 }
上面的就是一个Command的代码,注意,一个Command必需要实现ICommand接口,而如果是一个单Command的话,就需要继承SimpleCommand类,而如果是一个Command链的话,就需要实现MacroCommand,至于Command链,如果有J2EE基础的话,也就是Filter的过滤器链差不多。这里不多说。大家可以看看PureMVC的官方文档与API!
在Command里,都需要覆盖execute 这个方法,这个方法就是执行你的逻辑代码的地方,由Facade自动调用,当这个Command所绑定的消息被发送时,Facade就会创建一个Command实例并调用execute方法,方法里还传入一个INotification参数,就是你所发送的那个消息,里面包含了消息类型名称与消息体。在这个Command里,我没有处理什么,因为这个Command用于启动程序框架的,所以只在这里初始化了一些程序需要用到的资源,如注册代理与注册中介器,我们可以看到,注册代理与注册中介器的方法与注册Command的方法不同,注册Command的话,需要一个消息名称与一个Command类绑定,而代理与中介器的注册就不需要与消息绑定,直接将代理与中介器实例化之后进行注册就可以了。这是由于Command的生产控制不是由用户来操作的,是由Facade里面的一个工厂方法来对Command实例化并管理的,所以需要与一个消息名称进行绑定,而代理与中介器就是用户管理的,通常一个代理就对应一个数据结构,如果有几个数据结构都比较简单,就可以在一个Proxy里管理,而同理,中介器也一样,一个中介器对一个Flex组件,但为一个Button建立一个中介器未名太浪费了,所以我这里都是将MP3分成三个部分,控制按钮部分,歌曲列表部分,播放进度部分,三个部分用三个中介器。通常这些中介器或者Proxy创建一次就可以了,Facade会将它它存放在数组中,当需要用到时,再由Facade来获取他们,所以,在注册代理与中介的时候,先实例化它们再注册!因为Command是短生命周期,而Proxy与Mediator是长生命周期,所以这里与Command有点区别。在实例化中介器的时候,我传入一个app.controlBoard的值:
new ControlBoardMediator(app.controlBoard),就是说,我这个中介器是对应app.controlBoard这个控件,app就是主程序。
当所有需要的资源都注册好后,我执行了下面一句代码:
(facade.retrieveProxy(PlayListProxy.NAME) as PlayListProxy).loadPlayList();
之前讲到,注册代理时,将代理的实例进行注册,实际上就只是在Facade里将这个代理实例放进数组里而已,所以用facade.retrieveProxy()这个方法可以再次获得那个实例的引用,再调用这个代理里的一个方法loadPlayList()来进行加载播放列表。上上上面已经说过,因为Proxy是只可以发信息,不可以收信息,所以你叫Proxy工作的话,只好得到它的引用再调用它的方法来控件它的工作。注意,在Facade重新获得代理的方法里facade.retrieveProxy(PlayListProxy.NAME) as PlayListProxy 你需要指定一个字符串来获取某一个代理,在编写每一个代理时,都要为它指定一个name的字符串,而且是代理的唯一标识!这个时候,程序的流程就会跳到代理里运行loadPlayList()这个方法。下面,我们来看看PlayListProxy的代码:
1 package com.jiangzone.flex.pureplayer.model
2 {
3 import org.puremvc.as3.interfaces.IProxy;
4 //省略import代码
5 import flash.xml.XMLNode;
6
7 public class PlayListProxy extends Proxy implements IProxy
8 {
9 //定义一个代理的名字,唯一的标识
10 public static const NAME:String = 'PlayListProxy';
11 //定义一个HttpService,用于获取远程的数据
12 private var hs:HTTPService;
13
14 public function PlayListProxy():void{
15 super(NAME,new Array());
16 hs = new HTTPService();
17 hs.addEventListener(ResultEvent.RESULT,onResult);
18 hs.addEventListener(FaultEvent.FAULT,onFault);
19 }
20
21 public function get playList():Array{
22 return data as Array;
23 }
24
25 public function loadPlayList(url:String = 'jiang/pureplayer/data/playlist.xml'):void{
26 hs.method = "GET";
27 hs.resultFormat = "xml";
28 hs.url = url + '?ranid=' + (new Date()).time;
29 hs.send();
30 }
31
32 private function onFault(e:FaultEvent):void{
33
34 }
35
36 private function onResult(e:ResultEvent):void{
37 var arr:Array = data as Array;
38 var xmlNode:XMLNode = e.result as XMLNode;
39 for(var i:String in xmlNode.childNodes){
40 var obj:Object = new Object();
41 var node:XMLNode = xmlNode.childNodes[i];
42 obj["label"] = node.attributes.label;
43 obj["data"] = node.attributes.data;
44 arr.push(obj);
45 }
46 sendNotification("GET_PLAYLIST_COMPLETE",data);
47 }
48 }
49 }
2 {
3 import org.puremvc.as3.interfaces.IProxy;
4 //省略import代码
5 import flash.xml.XMLNode;
6
7 public class PlayListProxy extends Proxy implements IProxy
8 {
9 //定义一个代理的名字,唯一的标识
10 public static const NAME:String = 'PlayListProxy';
11 //定义一个HttpService,用于获取远程的数据
12 private var hs:HTTPService;
13
14 public function PlayListProxy():void{
15 super(NAME,new Array());
16 hs = new HTTPService();
17 hs.addEventListener(ResultEvent.RESULT,onResult);
18 hs.addEventListener(FaultEvent.FAULT,onFault);
19 }
20
21 public function get playList():Array{
22 return data as Array;
23 }
24
25 public function loadPlayList(url:String = 'jiang/pureplayer/data/playlist.xml'):void{
26 hs.method = "GET";
27 hs.resultFormat = "xml";
28 hs.url = url + '?ranid=' + (new Date()).time;
29 hs.send();
30 }
31
32 private function onFault(e:FaultEvent):void{
33
34 }
35
36 private function onResult(e:ResultEvent):void{
37 var arr:Array = data as Array;
38 var xmlNode:XMLNode = e.result as XMLNode;
39 for(var i:String in xmlNode.childNodes){
40 var obj:Object = new Object();
41 var node:XMLNode = xmlNode.childNodes[i];
42 obj["label"] = node.attributes.label;
43 obj["data"] = node.attributes.data;
44 arr.push(obj);
45 }
46 sendNotification("GET_PLAYLIST_COMPLETE",data);
47 }
48 }
49 }
来分析一下代码,在该代理被实例化时,会调用super(NAME,new Array());这个父类的方法,其实Proxy的构造函数接收两个参数,一个是Proxy的唯一标识名字,另一个就是所需要作代理的数据,是一个Object,就是说,你的这个Proxy类要对哪些数据作代理呢?就是这个值传入的,在Proxy基类里面有一个data的属性,这个属性就是存放你的实际数据,所以在我的这个PlayListProxy构造时,我创建了一个Array来作为我的数据,因为我这个代理,是代理一个播放列表数据的,所以我将用Array来存放我的播放列表数据。所以就将new Array()的值交给父类构造器里,父类构造器将会对该Array存放在data这个变量属性里。由于data是Object类型的,所以我们获取这个data的时候,还要对其转换成相应的类型,所以为了方便,我写了一个getter方法来封装这个操作:
1 public function get playList():Array{
2 return data as Array;
3 }
2 return data as Array;
3 }
以后可以直接调用playList属性来获取Array类型的数据了。
而在 loadPlayList 这个函数里,接收一个可选参数url,通过HttpService来获取该url中的xml数据。
按照上面说的那样,Proxy只有一个,但你可以按不同用途分为Local Proxy,与Remote Proxy,而这里它的作用就是Remote Proxy,因为他是操作远程数据。
当接收数据成功时,调用了 onResult 方法,里面进行了XML简单的分析,其实可以将该分析操作交给一个Command来完成,但是这里确实是太简单的分析了,就没有必交给Command了,自已来完成吧。当解析完成时,发送了一个消息sendNotification("GET_PLAYLIST_COMPLETE",data);
这里要说明一下,这里用了发消息来通知其它模块说我已获得数据了,你们谁对这个数据有兴趣就拿去吧。(本人觉得这样理解更有趣并容易理解)由于是这个机制,所以无论你界面怎样改变,中介器怎样改变,都不会影响到我Proxy,因为我获得数据时,我不用引用你某一个中介器(如果中介器改变了,则同时要修改Proxy的代码),我只发送一个消息,你们要则要,不要则罢,所以你界面如何改变的话,只要接收这个消息就可以同样的得到了数据,而不用因界面的修改而修改Proxy。这样就可以将Mediator与Proxy松耦了。
至于,是谁对这个“GET_PLAYLIST_COMPLETE”感兴趣呢?我在上上上面说过,Proxy只会发,不会收,所以肯定不是其它Proxy对这消息有兴趣,而Command么,Command感兴趣的消息都是在Facade里给绑定的,而我们上上上面的代码中,Facade只绑定了STARTUP这个消息,所以现在唯一下来的,就是中介器了,因为中介器也是可以收与发消息的。如果一个中介器(或者说一个界面组件吧)对一个消息感兴趣的话,那就需要在中介器的类里面定义这些感兴趣的消息,下面我们来看看一个ControlBoardMediator的中介器的代码:
1 package com.jiangzone.flex.pureplayer.view
2 {
3 import org.puremvc.as3.patterns.mediator.Mediator;
4 import org.puremvc.as3.interfaces.IMediator;
5 import com.jiangzone.flex.pureplayer.view.ui.SongListBoard;
6 import flash.events.MouseEvent;
7 import mx.events.ListEvent;
8 import org.puremvc.as3.interfaces.INotification;
9 import mx.controls.List;
10 import mx.collections.ArrayCollection;
11
12 public class SongListBoardMediator extends Mediator implements IMediator
13 {
14 public static const NAME:String = "SongListBoardMediator";
15
16 public function SongListBoardMediator(vc:Object):void{
17 super(NAME,vc);
18 songListBoard.songList.doubleClickEnabled = true;
19
20 songListBoard.songList.addEventListener(ListEvent.ITEM_DOUBLE_CLICK,onDoubleClick);
21 }
22
23 public function get songListBoard():SongListBoard{
24 return viewComponent as SongListBoard;
25 }
26
27 public function get songList():List{
28 return songListBoard.songList;
29 }
30
31 private function onDoubleClick(e:ListEvent):void{
32 var str:String = (e.target.selectedItem.data as String);
33 sendNotification("GET_SONG_URL_COMPLETE",str);
34 }
35
36 override public function listNotificationInterests():Array{
37 return [
38 "GET_SONG_URL",
39 "GET_PREV_URL",
40 "GET_NEXT_URL",
41 "GET_PLAYLIST_COMPLETE"
42 ];
43 }
44
45 override public function handleNotification(note:INotification):void{
46 switch(note.getName()){
47 case "GET_SONG_URL":
48 if(songList.selectedItem)
49
50
51 sendNotification("GET_SONG_URL_COMPLETE",songList.selectedItem.data as String);
52 break;
53 case "GET_PREV_URL":
54 if(songList.selectedItem){
55 var i:int = songList.selectedIndex;
56 i -= 1;
57 if(i<0) i = (songList.dataProvider as ArrayCollection).length - 1;
58 songList.selectedIndex = i;
59 sendNotification("GET_PREV_URL_COMPLETE",songList.selectedItem.data as String);
60 }
61 break;
62 case "GET_NEXT_URL":
63 if(songList.selectedItem){
64 var i:int = songList.selectedIndex;
65 i = (i + 1) % (songList.dataProvider as ArrayCollection).length;
66 songList.selectedIndex = i;
67 sendNotification("GET_PREV_URL_COMPLETE",songList.selectedItem.data as String);
68 }
69 break;
70 case "GET_PLAYLIST_COMPLETE":
71 var arr:Array = note.getBody() as Array;
72 songList.dataProvider = arr;
73 }
74 }
75 }
76 }
2 {
3 import org.puremvc.as3.patterns.mediator.Mediator;
4 import org.puremvc.as3.interfaces.IMediator;
5 import com.jiangzone.flex.pureplayer.view.ui.SongListBoard;
6 import flash.events.MouseEvent;
7 import mx.events.ListEvent;
8 import org.puremvc.as3.interfaces.INotification;
9 import mx.controls.List;
10 import mx.collections.ArrayCollection;
11
12 public class SongListBoardMediator extends Mediator implements IMediator
13 {
14 public static const NAME:String = "SongListBoardMediator";
15
16 public function SongListBoardMediator(vc:Object):void{
17 super(NAME,vc);
18 songListBoard.songList.doubleClickEnabled = true;
19
20 songListBoard.songList.addEventListener(ListEvent.ITEM_DOUBLE_CLICK,onDoubleClick);
21 }
22
23 public function get songListBoard():SongListBoard{
24 return viewComponent as SongListBoard;
25 }
26
27 public function get songList():List{
28 return songListBoard.songList;
29 }
30
31 private function onDoubleClick(e:ListEvent):void{
32 var str:String = (e.target.selectedItem.data as String);
33 sendNotification("GET_SONG_URL_COMPLETE",str);
34 }
35
36 override public function listNotificationInterests():Array{
37 return [
38 "GET_SONG_URL",
39 "GET_PREV_URL",
40 "GET_NEXT_URL",
41 "GET_PLAYLIST_COMPLETE"
42 ];
43 }
44
45 override public function handleNotification(note:INotification):void{
46 switch(note.getName()){
47 case "GET_SONG_URL":
48 if(songList.selectedItem)
49
50
51 sendNotification("GET_SONG_URL_COMPLETE",songList.selectedItem.data as String);
52 break;
53 case "GET_PREV_URL":
54 if(songList.selectedItem){
55 var i:int = songList.selectedIndex;
56 i -= 1;
57 if(i<0) i = (songList.dataProvider as ArrayCollection).length - 1;
58 songList.selectedIndex = i;
59 sendNotification("GET_PREV_URL_COMPLETE",songList.selectedItem.data as String);
60 }
61 break;
62 case "GET_NEXT_URL":
63 if(songList.selectedItem){
64 var i:int = songList.selectedIndex;
65 i = (i + 1) % (songList.dataProvider as ArrayCollection).length;
66 songList.selectedIndex = i;
67 sendNotification("GET_PREV_URL_COMPLETE",songList.selectedItem.data as String);
68 }
69 break;
70 case "GET_PLAYLIST_COMPLETE":
71 var arr:Array = note.getBody() as Array;
72 songList.dataProvider = arr;
73 }
74 }
75 }
76 }
在这份代码中,我们主要看看几个方法函数,其它的都只是处理逻辑,控制Sound的播放什么的,与PureMVC关系不大了。我们主要看看这几个方法:
override public function listNotificationInterests():Array
override public function handleNotification(note:INotification):void
先说第一个方法,这个方法返回的是一个数组,我们上面说到,怎么知道中介器对哪些消息感兴趣呢?就在这个方法里设置了,这些都是接口里的方法,所以需要override,在这个方法里,直接返回一个字符串数组就可以了,而那些字符串,就是你的消息名称 的字符串,刚才Proxy中,处理完数据后,发了一个“GET_PLAYLIST_COMPLETE”的消息,并将处理好的数据作为消息体一起发送出去了。
而在这个Mediator里的listNotificationInterests里,返回一个
1 return [
2 "GET_SONG_URL",
3 "GET_PREV_URL",
4 "GET_NEXT_URL",
5 "GET_PLAYLIST_COMPLETE"
6 ];
2 "GET_SONG_URL",
3 "GET_PREV_URL",
4 "GET_NEXT_URL",
5 "GET_PLAYLIST_COMPLETE"
6 ];
看到没有?那个数组里面包含了GET_PLAYLIST_COMPLETE这个消息名称字符串。所以,当有人发送这个消息时,这个中介器就会进行响应,至于对这个消息的响应程序,就在
override public function handleNotification(note:INotification):void
这个方法里面写上消息响应代码。
在响应函数里,都用switch(note.getName())来区分处理它所接收到的消息的名称,再处以不同的处理代码。
好了,基本上,整个PureMVC的结构就这样了,流程是比较简单,就是用消息(Notification)来围绕MVC各个部分模块来开发。这个程序还有其它的中介器类,代理类还有其它的类就不一一列出来了,反正PureMVC工作方式都是一样的,只是对应的功能逻辑不一样。
发表评论
-
Flex Label组件扩展边框与背景
2011-07-19 14:31 1049由于Label控件没有borderStyle属性,也就是它不支 ... -
Flash网游开发经验和框架
2011-07-06 14:26 950★FLASH WEB GAME的前端架构与人事分工→前端的主程 ... -
html5-websocket聊天例子
2011-07-01 18:13 1511http://html5demos.com/web-s ... -
关于在IE8中自动启用IE7兼容模式运行的代码
2010-09-09 08:28 971<meta http-equiv="X-UA- ... -
一个简单Ajax分页的实例
2010-08-08 12:11 16851.由前端页面发送Ajax请求至服务器端. 代码 ... -
Ajax中文问题彻底解决
2010-08-08 12:07 710中文问题是和做WEB的程序员形影不离的, jsp页面的中文问题 ... -
Javascript之Eval函数解析
2010-08-08 12:04 739Eval()函数 这是我在学习找到的一篇简述Eval的文章 ... -
关于EL标签嵌套迭代的问题
2010-06-12 14:02 13841.语法结构 ${expression} 2.[]与. ...
相关推荐
【标题】"PureMVC简单示例及其原理讲解"涉及的是一个开源的、跨平台的MVC(Model-View-Controller)框架——PureMVC。PureMVC以其轻量级和高度可扩展性,在开发中得到了广泛应用。这篇博客文章通过一个简单的示例,...
标题中的“可以运行的PureMVC的登陆实例”是指一个基于PureMVC框架的登录功能实现,这个实例已经经过验证可以在FlexBuilder3环境下正常运行。PureMVC是一种经典的多层应用架构模式,它为ActionScript、JavaScript、...
PureMvc是一个开源的、轻量级的框架,用于构建多层结构的富客户端应用程序。它遵循Model-View-Controller(MVC)设计模式,并提供了一种标准化的方式来组织和协调应用程序的各个部分。在这个名为"MyFirstPureMvc"的...
**PureMVC框架详解** PureMVC是一种轻量级、模型-视图-控制器(Model-View-...通过学习和实践"PureMVCExample"中的示例,你可以深入理解PureMVC的工作原理,并将其应用到自己的项目中,提升开发效率和代码质量。
描述 "Flex framework,又一个新的flex界面框架" 暗示PureMVC 是与Adobe Flex相关的,Flex是用于创建交互式、高性能的RIA的开发工具包,它使用MXML和ActionScript进行开发。而PureMVC作为一个框架,为Flex应用程序...
纯MVC(PureMVC)是一个轻量级的框架,用于构建基于模型-视图-控制器(Model-View-Controller)设计模式的应用程序。...通过研究这个示例,你可以更好地理解PureMVC的工作原理和如何在实际项目中应用。
这个“PureMVC简单例子”是为了帮助初学者理解PureMVC框架的基本原理和实际操作流程。在这个例子中,我们将深入探讨各层的职责以及它们之间的通信机制。 首先,我们来了解一下MVC模式。MVC模式是一种软件设计模式,...
压缩包内的中文文档提供了PureMVC框架的详细解释,包括基本概念、使用方法、示例代码和最佳实践,对于初学者来说是非常有价值的参考资料。 通过学习和使用这个PureMVC实例及中文文档,开发者能够更好地理解如何在...
Unity3D客户端PureMVC框架视屏讲解是一个深入解析如何在Unity3D环境中应用PureMVC框架的教学资源。PureMVC是一种轻量级、模块化的前端框架,它为游戏开发提供了一种组织代码的结构,使得项目更易于维护和扩展。在...
《PureMVC登录示例详解》 在软件开发中,框架的选择往往对项目的架构和可维护性起到关键作用。PureMVC,一个轻量级、跨平台的MVC(Model-View-Controller)框架,因其简洁的设计和强大的组织能力,受到了许多开发者...
6. **Larena2.0与3.0APP框架差异——pureMVC架构的解析与仿制.ppt**:这个文件可能详细分析了Larena应用程序框架在2.0和3.0版本中如何采用PureMVC架构,并可能讨论了它们之间的差异和改进之处。 7. **pureMVC类成员...
5. **实际应用**:提供示例代码和案例,展示如何在实际项目中运用PureMVC,例如创建一个新的模块、处理用户交互、数据管理等。 6. **扩展与优化**:可能会涉及如何自定义PureMVC组件以适应特定需求,以及如何与其他...
在"puremvc-csharp-standard-framework-master"这个压缩包中,你将找到PureMVC C#标准版的源代码,包括实现MVC模式的各种接口和类,以及示例应用,这些都是理解和使用PureMVC框架的关键资源。通过深入研究这些文件,...
- 示例代码:PureMVC官方仓库包含不同语言版本的示例项目。 - 开发者社区:加入PureMVC开发者社区,与其他开发者交流经验。 通过理解和实践PureMVC AS3,开发者能够构建出高效、模块化的ActionScript 3应用程序,...
"PureMVC入门与提高资料"这个压缩包可能包含了各种教程、示例代码和文档,可以帮助初学者快速理解PureMVC的工作原理和用法。通过阅读文档,研究示例,以及实际动手编写PureMVC应用,可以深入掌握这一强大的框架。 ...
6. **Hello World示例**:通常会通过一个简单的“Hello World”程序来演示PureMVC的基本工作流程,展示如何创建并运行一个完整的MVC循环。 7. **源码分析**:可能对PureMVC的源码进行解析,帮助理解其内部机制和...
这个是一个根据AS3(ActionScript 3) pureMVC而转换过来的lua pureMVC。所有的接口完全跟AS3版本一致。 若是想使用,可以直接查看网上的pureMVC 文档,我并未对任何一个函数改名或者更换参数位置。 注意,这个PureMVC...
标题中提到的"PureMVC各种例子"是指PureMVC框架提供的一系列示例项目,这些例子可以帮助开发者快速理解如何在实际项目中应用PureMVC。通过这些实例,你可以学习到如何组织和协调模型、视图和控制器组件,以及如何...
**Qt与PureMVC的结合** 在Qt环境中,PureMVC提供了对C++语言的支持,使得开发者可以利用其强大的功能和面向对象的特性,同时利用PureMVC的组织结构。这个版本的PureMVC允许开发者更专注于业务逻辑,而不是关注如何把...
在本教程中,我们将深入探讨PureMVC的核心概念、设计模式以及如何通过提供的源码和示例来学习和应用它。 **1. 观察者模式(Observer Pattern)** PureMVC的核心是观察者模式,它允许各个组件之间松耦合地通信。在...