Flex观察者模式

来源:互联网 发布:ds18b20与单片机连接图 编辑:程序博客网 时间:2024/06/05 19:15
假设以下场景:
Application中有两个Panel:PanelA和PanelB,其中PanelA是主界面,用到的数据模型为ModelA,PanelB是从PanelA跳转过来的,用到的数据模型为ModelA和ModelB。
分为三个步骤:
  1. PanelA请求数据。
  2. PanelA和PanelB处理数据。
  3. 用户操作从PanelA跳转到PanelB。

这里只讨论使用设计模式的情况,那些不使用设计模式的方法不在谈论之列。

Cairngorm
Cairngorm是一个比较简单的微框架,基本思想是把命令的请求者和命令的执行者分开。
请求者通过发送CairngormEvent来执行命令,而执行者就是一个Command,而它们之前的关联关系则通过FrontController进行初始化,隐藏在FrontController后面的则是一个单例的CairngormEventDispatcher类。
Cairngorm的这种方式就是一个典型的观察者模式:
  1. Actionscript3本身就有个观察者模式的实现——EventDispatcher。
  2. Cairngorm对其进行了一层封装,把观察者模式中的Subject(即Actionscript3中的EventDispatcher)封装到CairngormEventDispatcher中,而观察者模式中的attach/detach操作则封装在FrontController中。
  3. 而Subject的调用方就是上面说的命令的请求者。
回到上面的场景的步骤1,PanelA请求ModelA的数据时需要发送一个CairngormEvent,而对应的Command接收到ModelA后,有两个方向:
  1. Command把ModelA放到PanelA和PanelB中。
  2. Command通知PanelA和PanelB数据已经准备好在某个地方,PanelA和PanelB去某个地方获取ModelA。
如果用方法1,Command需要知道PanelA和PanelB的引用,实现这个有很多方法(如通过全局变量存储、PanelA和PanelB的引用通过CairngormEvent对象传到Command中),明显,这些都不是好方法,最重要的一个原因是Command需要知道使用ModelA的具体对象。
方法2,就是一观察者模式。Command不需要知道ModelA的具体使用对象,不管是PanelA、PanelB,还是后来多了一个PanelC。Command只需要发送一个完成的通知即可。

到这里可以看到,一个视图要执行一个命令(步骤1)是通过发送一个CairngormEvent,以此将视图和Command实现松耦合,而现在(步骤2)则需要Command发送一个通知所有需要使用ModelA的视图。

PureMVC
PureMVC是另外一个常用的Actionscript3 MVC框架,但使用上要比Cairngorm复杂得多。故我一直比较抗拒PureMVC。
  1. PureMVC对于每个视图View都需要创建一个对应的Mediator,对于每个模型Model都需要创建一个对应的Proxy,外加若干个Command作为控制器。
  2. 调用时是通过一个单例Facade对象(多核心版的PureMVC实现了多个单例,但我觉得使用更麻烦了)。
  3. 所有的Mediator、Proxy、Command都可以互相调用,内部关系非常复杂,要理清一个比较大的项目的Mediator、Proxy、Command之间的调用关系不是那么容易的。
对于步骤1和步骤2,如果使用PureMVC,首先PanelA和PanelB分别对应一个Mediator,ModelA和ModelB分别对应一个Proxy:
  • Facade初始化时先依次注册Command、Proxy和Mediator。
  • Facade发送通知,执行Command。
  • Command初始化Proxy的属性。
  • Proxy发送通知。
  • Mediator接收到Facade发送过来的通知,通过Proxy获取到Model,然后更新对应的Panel。
=====================================================================================
我在面试那些应聘Flex开发职位的应聘者的时候,如果他了解PureMVC,我除了问PureMVC的基本框架思想外,我都会问两个问题:
  1. 你觉得PureMVC为什么叫Pure?
  2. PureMVC的notification和Cairngorm的CairngormEvent的区别是什么?
这两个问题其实是相通的。
=====================================================================================
我的做法
对于文章一开始的场景的设计,步骤1(发送命令请求)我个人比较推崇用Cairngorm,而步骤2(接收返回数据并展现)我习惯于利用EventDispatcher。
PureMVC由于没有利用Actionscript3中内置的观察者模式的实现(EventDispatcher),而是创造了notification这种与语言无关的实现。
使用EventDispatcher,对于步骤2的实现,也可以有两个途径:
  1. EventDispatcher放到模型中。
  2. EventDispatcher放到一个独立的中介者Mediator中。
EventDispatcher放到模型中
ArrayCollection就内置了一个EventDispatcher,当List持有一个ArrayCollection对象,如果ArrayCollection对象发生了改变,则List会做出相应的变化。
步骤2中,PanelA和PanelB就必须在调用请求命令之前就持有ModelA的对象,而Command只是修改ModelA的属性值。
EventDispatcher放到一个独立的中介者Mediator中
如果PanelA和PanelB在调用请求命令之前没有持有ModelA的对象,则Command可以把创建的ModelA对象传送给Mediator,Mediator通知PanelA和PanelB,而一个持有ModelA对象的Event对象需要贯通这个过程。具体流程如下:
  • PanelA和PanelB通过Mediator监听事件eventA,并注册监听方法funcA和funcB。
  • Command创建ModelA后,创建事件eventA,eventA持有ModelA对象,并通过Mediator发送出去。
  • Mediator接收到eventA后,调用观察者PanelA和PanelB的注册方法,即funcA(eventA)和funcB(eventA)。
  • funcA更新PanelA,funcB更新PanelB。
方法二可以把方法一合并进来,即ModelA中内置一个EventDispatcher,PanelA和PanelB就可以监听ModelA的变化。
步骤3
步骤3是两个视图之间的交互,显而易见,还是可以利用上面用到的Mediator实现松耦合。

总结
  1. 设计模式只是一种工具,一种经验积累的成果,不能为了模式而模式。
  2. 不能局限于设计模式,视图之间的交互不一定用中介者模式,也可以用观察者模式,或者其它模式,只要模式合适。
  3. 要认清PureMVC的优缺点,轻易别用PureMVC。
  4. 可以多使用和参考Actionscript3内置的一些特点和Flex的一些官方做法。