7月 30

两年前看了Kevin Cao关于Build website using Gaia Flash Framework + Robotlegs + Signals的系列文章,于是开始关注Robotlegs,没太看明白它的官方最佳实践,那时候对什么元数据编程啦,MVC框架啦,依赖注入控制反转啦,完全一片空白,懵了。
后来又看了SwiftSuspenders 1.6浅出深入,也没看明白。
后来把那两本AS3设计模式书翻了下,懂了点。
因为Kevin Cao对依赖注入控制反转,很推崇,我也一直留心。
做了一年游戏后,乘着换工作的时间,再次挑战依赖注入控制反转,再看之前那些文章,发现好多地方已经理解了。有些东西第一次看不明白,也要硬着头皮看完,过段时间再看时,就会发现没有第一次那么困难了。
说到依赖注入控制反转,其实就是工厂模式和反射编程的结合。
就像有的文章说的,可以把IoC模式看做是工厂模式的升华,可以把IoC看作是一个大工厂,只不过这个大工厂里要生成的对象都是在XML文件中给出定义的,然后利用“反射”编程,根据XML中给出的类名生成相应的对象。从实现来看,IoC是把以前在工厂方法里写死的对象生成代码,改变为由XML文件来定义,也就是把工厂和对象生成这两者独立分隔开来,目的就是提高灵活性和可维护性。
一说AS3的依赖注入控制反转,立马就会谈到元数据编程,但是就如SwiftSuspenders 1.6浅出深入中说的,基于元数据的注入如果需要修改,需要重新编译源码,这也有一些违背依赖注入设计的一些初衷。
元数据是AS3的特有一种特性。本质上AS3的元数据就是在AS类的XML描述中添加一些自定义的标签。所以本质上,swiftsuspenders还是一个基于XML进行依赖注入配置的框架。基于XML的配置实际上相当于手动添加元数据信息。
要了解AS3的元数据和依赖注入控制反转,首先要知道的是,flash.utils.describeType,其次是flash.utils.getDefinitionByName,这是关键中的关键。
看了那么多文章,还是《依赖注入那些事儿》对我帮助最大,那个是用c#做的示例,我把它的示例代码改写成了Flash ActionScript 3的。
这里有些疑问的是,c#的load难道都是同步的?看他的代码,无论是 xmlDoc.Load还是Assembly.Load,都是直接就用了,我都懵了。在Flash里还要等待加载完成,接收到异步事件后才能使用。实在没能在AS3中找到能像Assembly.Load那样的方法。
所以控制反转那个示例,我写了两版,第一版还是用XML,但是如不事先声明类定义,getDefinitionByName会无法在应用程序域中找到类定义。没C#牛呀。
后一版,我直接弄了个Config的swf,把它直接加载到当前应用程序域了,也就不会被垃圾回收,省了在加载。这样勉强算是实现了彻底的解耦,以后要改类配置,要加类定义,只需重新编译Config.swf就行了,其实和改Config.xml一样,不用重新编译整个项目。
写完后悲催发现,实际项目中估计用不到,反复看了Robotlegs和swiftsuspenders,就算它支持XML配置,可是还是要先用那些map…类的方法,把类绑定进去呀,不然一样无法getDefinitionByName到类。那些map…之类的方法也不太可能封装到一个类似Config的swf里。要用Robotlegs最多像控制反转的XML版那样了。
这里只给出控制反转的XML版,其他的可以去我的github看。,就是ASDependency那几个项目。
ASDependency.as

package {
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.net.URLLoader;
	import flash.net.URLRequest;
	import flash.text.TextField;
	public class ASDependency extends Sprite {
		public function ASDependency() {
			MacButton;
			WindowsButton;
			var loader : URLLoader = new URLLoader();
			loader.load(new URLRequest("Config.xml"));
			loader.addEventListener(Event.COMPLETE, _loaderCompleteHandler);
		}
		private function _loaderCompleteHandler(event : Event) : void {
			var loader:URLLoader = event.currentTarget as URLLoader;
			var config : XML = new XML(loader.data);
			var btn : IButton = FactoryContainer.MakeButton(config);
			var txt : TextField = new TextField();
			txt.text = btn.showInfo();
			addChild(txt);
		}
	}
}

FactoryContainer.as

package {
	import flash.utils.getDefinitionByName;
	public class FactoryContainer {
		public static function MakeButton(config : XML) : IButton {
			var node : String = String(config.factory.button[0]);
			var ClassReference : Class = getDefinitionByName(node) as Class;
			var instance : IButton = new ClassReference();
			return instance;
		}
	}
}

IFactory.as

package {
	public interface IFactory {
		function makeButton() : IButton;
	}
}

MacButton.as

package {
	public class MacButton implements IButton {
		private var description : String;
		public function MacButton() {
			description = "Mac风格按钮";
			trace(description);
		}
		public function showInfo() : String {
			return description;
		}
	}
}

WindowsButton.as

package {
	public class WindowsButton implements IButton {
		private var description : String;
		public function WindowsButton() {
			description = "Windows风格按钮";
		}
		public function showInfo() : String {
			return description;
		}
	}
}

IButton.as

package {
	public interface IButton {
		function showInfo() : String;
	}
}

Config.xml



	
		
	

written by panhezeng \\ tags:

Leave a Reply

Me

点这和我talk,panhezeng@gmail.com