存档

文章标签 ‘PureMVC’

as3signals tutorial

2010年2月26日 ColorHook 没有评论

用于Flash平台的开源事件模型as3signals目前在开源社区逐步升温,我在之前的一篇Blog中也略有提到了它诞生的历史原因。如今,许多著名的Flash框架开始与as3signals联姻,这里是它们的一些结合体:

跟EventDispathcer相比,as3signals有更易用的接口,如add, remove, dispatch。并且它还有一些EventDispatcher没有的接口,如addOnce, removeAll。

下面看看如何使用as3signals来触发和监听事件:

public class Dispatcher{
 
	public var signal:Signal;
	public function Dispatcher(){
		signal=new Signal();
	}
}
 
var dispatcher:Dispatcher=new Dispatcher();
dispatcher.signal.add(signalHandler);
function signalHandler():void{
	trace("signal dispatched");
}
dispatcher.signal.dispatch();

ISignal.dispatch()支持可变参数,这意味着通信时可以传递任意数量的对象。Signal的构造函数可以指定dispatch传输的数据类型,这利于在编译期间找出潜在bug。

public class Dispatcher{
 
	public var signal:Signal;
	public function Dispatcher(){
		signal=new Signal(String,String);
	}
}
 
var dispatcher:Dispatcher=new Dispatcher();
dispatcher.signal.add(signalHandler);
function signalHandler(p:String,q:String):void{
	trace("signal dispatched");
}
dispatcher.signal.dispatch("cool","as3Signals");

DeluxeSignal支持事件监听器的优先级以及事件的冒泡。addOnce监听的事件只会执行一次,这将为我们提供很大的便利。

var deluxeSignal:DeluxeSignal = new DeluxeSignal(this);
deluxeSignal.addOnce(deluxeSignalHandler);
deluxeSignal.dispatch(new GenericEvent());
deluxeSignal.dispatch(new GenericEvent());
 
function deluxeSignalHandler(event:GenericEvent):void {
	trace("deluxeSignal dispatched: ",event.target);
}

NativeSignal用于结合EventDispatcher,这样原本有EventDispatcher发出的事件转交给了Signal。

var nativeSignal:NativeSignal = new NativeSignal(stage, MouseEvent.CLICK, MouseEvent);
nativeSignal.add(nativeMouseHandler);
 
function nativeMouseHandler(event:MouseEvent):void {
	trace("nativeMouseHandler: stage clicked");
}

除了易用的接口之外,它在性能上也表现不凡:http://alecmce.com/as3/events-and-signals-performance-tests

应用在PureMVC js框架中的基础库Objs

2009年9月1日 ColorHook 没有评论

Objs是一个轻量级的JavaScript库,可以使用它来在代码中组织类和接口。这个库应用在大名鼎鼎的PureMVC的JS版本中。看看Objs在JavaScript如何发挥它的作用。

创建一个接口
function class_demo_IParent(){
	Objs.register("demo.IParent",IParent);
	function IParent(){
	}
	var o=IParent.prototype;
	o.getName=function(){};
	o.setName=function(value){};
};

接口和类定义在一个函数中,这个函数名以”class”开始,然后跟着包名和函数名。创建一个接口或者类,需要在Objs中注册,Objs是一个全局的管理者。

创建一个类
function class_demo_Parent(){
	Objs.register("demo.Parent",Parent);
 
	var IParent=Objs.load("demo.IParent")
	function Parent(name){
		if(Objs.extending) return;
		this.name=name;
	}
	Objs.implement(Parent, IParent);
	Parent.prototype.setName=function(value){
		this.name=value;
	}
	Parent.prototype.getName=function(value){
		return this.name;
	}
};

类的创建跟接口基本是一样的,Javasript本身没有类和接口的概念,它们都是用function来模拟的。这个类实现了上面的接口demo.IParent,使用Objs.load来“加载”一个接口或类,然后用Objs.implement来实现一个接口。

可以发现在类的构造函数中最开始的部分都加了一句“if(Objs.extending) return;”,这句的作用是当将父类的构造函数制定在子类的饿prototype上时,内面的逻辑不会重复执行。

创建另一个接口
function class_demo_ISub(){
	Objs.register("demo.ISub",ISub);
	function ISub(){
	}
	var o=ISub.prototype;
	o.getAge=function(){};
	o.setAge=function(value){};
}
创建子类
function class_demo_Sub(){
	Objs.register("demo.Sub",Sub);
 
	var Parent=Objs.load("demo.Parent");
	var IParent=Objs.load("demo.IParent");
	var ISub=Objs.load("demo.ISub");
	function Sub(name,age){
		Parent.apply(this,[name]);
		if(Objs.extending) return;
		this.age=age;
	}
 
	Objs.extend(Sub,Parent);
	Objs.implement(Sub,IParent);
	Objs.implement(Sub,ISub);
 
	var o=Sub.prototype;
	o.getAge=function(){
		return this.age;
	};
	o.setAge=function(value){
		this.age=value;
	};
}

创建一个子类来继承demo.Parent,并实现另一个接口。子类的构造函数中需要调用父类的构造函数。并使用Objs.extend来实现继承。如果要override一个方法并保留父类的逻辑,则也需要在方法中调用父类的同名方法。

测试代码
var Parent=Objs.load("demo.Parent");
var Sub=Objs.load("demo.Sub");
var daMao=new Parent("Da Mao");
var xiaoMao=new Parent("Xiao Mao",8);
console.log(daMao.getName()); //Da Mao
console.log(xiaoMao.getName()); //Xiao Mao
console.log(xiaoMao.getAge()); //8

如何选择一个Flex框架

2009年6月30日 ColorHook 没有评论

翻译自:http://www.adobe.com/devnet/flex/articles/flex_framework_02.html

Cairngorm

Cairngorm是一个广为人知的老牌Flex框架。它是一个微型架构——由一些设计模式组成用来降低团队协作的困难。

Cairngorm从Java的世界带来了很多开发理念,并且把重点放在三个关键区域:处理用户动作,封装服务端的交互和业务逻辑,管理客户端的状态和界面呈现。

使用Cairngorm来构建一个项目,需要将应用代码分离到不同的包并且继承Cairngorm的类。以下是Cairngorm项目中一些主要的部分和类。

  • ModelLocator是一个储存数据的单例,数据表示程序的状态。单例类的性质保证了程序中的所有组件取得的是相同的数据。
  • ServiceLocator是另一个单例,它集中管理所有服务如HTTPServices。同样,由于是单例,程序中的所有组件取得的是相同的服务。
  • 业务逻辑被封装在command类中。command实现了命令模式,它们表示相应用户事件的逻辑。
  • 事件被类FrontController处理,FrontController会把事件映射到相应的Command。
  • Delegate类作为代理来对远端服务进行请求和响应。
优点

Cairngorm在Flex社区广为人知,作为Adobe开源项目的一员,拥有活跃的社区和开发者的支持。

其次,该框架吸取了Java开发中许多宝贵的经验,并成功得用于大型项目的开发中。

并且,Cairngorm适用于团队开发,因为它提供了结构化的开发方法来创建应用,利于分布式的开发。

缺点

需要写大量的类应该是Cairngorm最多的负面评论了。在Cairngorm中,每一个event对应一个command;因此,需要对程序触发的每一个事件来写一个command类。而且,还要为command写一些其他的类,例如delegates。即使是一个中型的应用也会导致大量的类产生。

其次,Cairngorm实现了自己的一套事件处理的方法。这增加了Flex内置事件模型的复杂度,而且它还有限制。由于每个事件都有自己的的command,事件的响应者被限制成1个。加之Cairngorm的事件不具冒泡特性,如果要发送数据到容器的其它层次则需要自己来实现。

第三个常见的批评是Cairngorm依赖全局的单例,这让模块和单元测试变得困难。尽管可以打破单例中的模型简化测试,但是会增加额外的过程。

资源

Mate

Mate是一个基于标签的,事件驱动的框架。基于标签意味着它可以完全实现在MXML中。该框架的目的是让事件响应者的声明变得简便。

在项目中使用Mate只需要处理两个方面:使用1个或者多个事件,有一个成为”event map“的MXML文件——被包含在主程序中的一个MXML文件。它定义了需要监听的事件以及如何被处理。必须有1个event map,而且允许有多个。

Mate也实现了依赖注入(Dependency injection)的理念——有时被称为好莱坞原则,或 “don’t call us, we’ll call you”。对象的创建时这样一种方式:数据被创建并且注入到对象中。也就是说,对象不会喊着要数据(”don’t call us”),而是数据被传送给对象(”we’ll call you”)。

优点

Mate使用依赖注入提升了松耦合性。因为组件不依赖全局的单例,能更自由地作为对立的部分。Mate不会阻止你使用Flex内建的事件机制,也不会像Cairngorm一样为每个事件都使用单独的响应。Mate的MXML标签文件简单易用,而且文档优秀,在官网上有大量的代码实例。

缺点

Mate使用MXML文件构建,要是作为一个ActionScript开发者,就需要调整自己的习惯。而且Mate没有为应用程序制定结构,这份工作留给了开发者。

因此,需要加强团队协作来保证代码的兼容性。还有一个问题与Adobe LiveCycle Data Services ES有关,要知道Mate暂时还不能处理LiveCycle Data Services提供的数据管理方面的功能。

资源

PureMVC

尽管PureMVC用在Flex上,但是它并不是只为Flex设计的。PureMVC的创建者想让它是一个语言无关的框架。如果你访问它的网站,会发现大量的不同语言的实现版本。

PureMVC以MVC模式为中心,其目标是把项目分离成模型层,视图层和控制层。这三个层表现为三个单例——ModelViewController,还有第四个单例Facade用来对前三个单例进行集中管理,是Facade模式的实现。

与Cairngorm很像,使用PureMVC创建一个项目需要把项目分成多个包,然后继承框架中的类来构造自己的类。最后还要为项目额外创建一个Facade类来作为程序的入口。

优点

与Cairngorm一样,PureMVC是一个结构良好的框架,有活跃的社区和开发者支持。它很适合团队开发,其清晰的结构能告诉开发者如何创建和组织代码。

缺点

因为它依赖于单例,所以有着和Cairngorm一样的缺点。它不是一个特定的Flex框架,所以没有充分利用到MXML的特性。

跟Cairngorm类似,PureMVC有自己的事件处理方式,但是跟标准的Flex事件模型一起工作会增加开发难度。

PureMVC是一个比较复杂的框架,有相当陡的学习曲线。除非你的团队很熟悉它,否则培训会占用很多时间。

还有,PureMVC也需要创建很多类,既增加了产品的开发时间,又增大了项目的尺寸。

资源

Swiz

Swiz是一个控制反转(IoC, Inversion of Control)框架,它提供一些机制来简化事件处理和异步远程调用。Swiz的真正意图是以一种简单高效的方式提供一个MVC范式。与Cairngorm和PureMVC不同,它借鉴了Java的一些模式,摒弃了预定义的文件结构。

使用Swiz创建一个项目需要告诉Swiz所用到的组件。以这个为核心,Swiz是一个集中管理的工厂模式。组件被名为BeanLoader的静态类加载到工厂当中,由工厂来处理组建的实例化。

Swiz还提供依赖管理,它使用了一个名为Autowire的自定义标签,Autowire标签定义依赖然后交给Swiz处理。

优点

Swiz简单易用,没有预定义的文件结构。类似于Mate,Swiz通过Autowire这个依赖注入系统,提升了松耦合性。也类似于Mate,它使用Flex内建的事件模型,并且使用单例来发送一个关键的事件。

缺点

跟Mate一样,Swiz没有为项目的结构做过多的定义,这些留给了开发者,因此,需要加强团队协作来保证代码的兼容性。

其次,它使用了自定义标签,项目的建立会额外多出一些步骤,例如设置额外的编译选项。这些过程并不复杂,但是至少这些过程在其他框架中不需要。文档强调的是Flex 2的开发者,所以可能不适合比Flex 2更新的版本。

资源

做出选择

虽然描述的并不详尽,但是这些信息加上资源足以让人理解提到的每个框架的方法论,优点,还有缺点。看了这些,你将如何作出取舍呢?

也许第一个问题应该问:我是否需要一个框架?Flex和MXML为快速应用开发提供了健全的系统和方法。我一直以来不太使用框架的原因是,相对于使用Flex框架而言,使用额外的框架会让我为了适应这些框架而去做更多的事情。我认为,框架的作用是简化工作任务和提高生产率,而不是为了证明我能用或者用了就说明我是一个优秀的开发者。

在一个电话面试中,我解释了自己为什么选择不使用框架,面试者回应:”我们是一个大的团队,所以你明白为什么我们需要一些框架了”。一番思索之后,我确实明白了它的意思。

使用框架的一个好处就是它让代码的编写标准化了。一个程序员A和一个程序员B使用同一个框架负责同一个项目的两个不同部分,那么可以认为他们写的程序是兼容的。也许这时候应该考虑另一个问题:有多少结构允许被强加?

这里介绍的这些框架或多会少都包含了一些预定义的结构。与独自开发相比,团队开发需要更多这样的结构。这些结构可以增加项目的开发时间和文件尺寸,但是也会提升团队的开发环境和代码的一致性。相比这下,如果你是项目唯一的开发者,就不需要把事情搞那么复杂,或许你需要一个没有这么多预定义结构的框架。

所以,选择一个正确的框架或者压根不用框架是由开发环境和项目决定的。我能给出的最好的建议是了解你的项目。通过我的调查和这篇文章,我认为自己对框架的看法会更深刻,它们确实可以满足一些需求。

观察者和事件驱动

2009年5月27日 ColorHook 没有评论

观察者是一个应用很普遍的设计模式,与请求-响应的方式不同,观察者模式以发布-订阅的方式来进行消息传递。

好莱坞有句名言常常被用在软件设计上,那就是:Don’t call us, we will call you. 观察者就如好莱坞原则一样强调职责的转移,你想进好莱坞吗?如果是也不用为了知道好莱坞是否需要人而天天往那跑,你要做的是留下电话号码,当真正需要人手的时候,好莱坞的工作人员就会主动联系你。显而易见,这样给你节省了大量的时间。

观察者模式的思想就是基于这样一个模型,以PureMVC中的观察者为例,通常该模型中的涉众有:

1. 通知者 INotifier

通知者可以注册观察者,退订观察者,发布事件。

  • sendNotification(notification)
  • register(notificationName, observer)
  • unregister(notificationName, observer)
  • 2. 观察者 IObserver

    观察者可以处理相应的事件

  • handleNotification(notification)
  • 3. 通知 INotification

    通知是一则消息,一则事件,它可以携带数据,也可以根据它得知事件类型(type)和事件源(INotifier)。

  • getType()
  • getTarget()
  • 通常观察者模式注册一个通知需要一个IObserver类型的对象,这样通知产生的时候直接调用抽象接口的方法来做相应的处理,虽然接口是极度抽象的,但是不可避免地增加了接口之间的依赖程度。

    事件驱动是以观察者模式为核心,并在此基础上增加了事件管理器来对事件进行转化、排序、分派等,应用范围也是极其广泛。

    对比上述观察者模式,ActionScript中的事件驱动将涉众从3个缩减到2个,没有了观察者这个角色,而是以回调函数的形式来处理事件,这意味着任何对象都有可能从中收益,进一步降低了系统的耦合度。

    1. IEventDispatcher

    事件发送者发布事件,注册监听器和移除监听器。

  • dispatchEvent(event)
  • addEventListener(eventName,callback)
  • removeEventListener(eventName,callback)
  • 2. Event

    类似于通知,它可以携带数据,也可以根据它得知事件类型(type)和事件源(INotifier)。

  • getType()
  • getTarget()