newland.js的Ioc实现
newland.js之所以是框架而不是类库,最重要的原因是它只要几步就能建立起一个网站!换言之,它已经包含了网站从启动到运行的重要流程,至于你还需要什么,是制定还是完善等等不确定的东西,框架已经预留了位置给你填空了。
框架让程序员变成流水线上的工人这一事实是不可逆转的,唯一的区别的有的流水线非常智能,让你过得非常惬意,有的则非常恶心,让你变成苦逼的码畜!
JAVA的三大框架之一,Spring最伟大的举措就是发明了IoC容器。是的,框架帮你做了许多事,但有些文件还是要你去建的,建在指定的目录下;有些代码你还要去写,依照规定好的格式,是建哪个类,继承哪个父类,差一步也不行。框架带来便捷的同时,也带来的枷锁!IoC的目的是让这些痛苦减少些!
首先抄一段话解释什么叫IoC——“在依赖注入的模式下,创建被调用者的工作不再由调用者来完成,因此称为控制反转;创建被调用者实例的工作通常由容器来完成,然后注入给调用者,因此也称为依赖注入”。
在我的newland.js框架中,最开始的mass.js模块其实只是提供模块加载机制与一些通用方法,不干正事的,它唯一做的有意义的是调用了mvc这个模块。
$.require("system/deploy,system/mvc", function(deploy){ deploy( process.cwd() );//监听app目录下文件的变化,实现热启动 });
mvc模块的作用是启动服用器,接受请求!在此之前,它会读取配置文件,把用户指定的拦截器与默认拦截器与所有控制器统统读入内存,装配到系统中!
所谓的栏截器其实就是structs那套东西,不认识JAVA的同学如果懂node.js,就去看看connectjs的中间件,差不多就是这个意思,用于处理请求与响应用的工具包。
而控制器是重点,MVC不能没有它。一个控制器有许多功能,因此我们把共用的功能抽取出来构建成基类, 而我们所建的控制器作为子类继承它就可以节约许多代码。
//遍历app/controllers目录下所有控制器模块,并与拦载器模块一起加载它们! $.walk("app/controllers", function(files){ $.require( inter.concat( files ), function(){ //拦截器放在最前面 var intercepters = [].slice.call(arguments,0, inter.length); resource_ready( intercepters ) }); });
在没有用IoC之前,我们的控制器是这个样子的:
.define("doc_controller",function(){ var klass = $.factory({ inherit: $.base_controller, index: function(flow){ $.log("已进入doc#index action"); flow.session.set("xxx","in doc") var view_url = $.path.join("app","views", flow.req.url ); flow.fire("get_view", view_url, flow.req.url ) } }); $.controllers[ "doc"] = new klass });
其实用户要写的就是index,show, edit, destroy这样action方法,其他部分就是死格式!什么指定父类,new实例其实交给系统去处理就行了!因此我们的自定义控制器可以削减成这样子!
$.define("doc_controller",function(){ return { index: function(flow){ $.log("已进入doc#index action"); flow.session.set("xxx","in doc") var view_url = $.path.join("app","views", flow.req.url ); flow.fire("get_view", view_url, flow.req.url ) } } });
那么子类的创建与继承关系的指定与实例的创建传到mvc.js中去管理:
var rcname = /\/(\w+)_controller/; //遍历app/controllers目录下所有控制器模块,并与拦载器模块一起加载它们! $.walk("app/controllers", function(files){ $.require( inter.concat( files ), function(){ //取得放在前面的拦截器 var intercepters = [].splice.call(arguments, 0, inter.length); //取得放在后面的控制器 var controllers = arguments; //进行控制反转,构建我们所需要的控制器子类与它的实例 files.forEach(function(el, i){ //mac下的路径为 app/controllers/doc_controller.js //window下的路径为 app\\controllers\\doc_controller.js var match = el.replace(/\\/g,"/").match(rcname); var controller = controllers[i]; controller.inherit = $.base_controller var klass = $.factory(controller); $.controllers[ match[1] ] = new klass; }); resource_ready( intercepters ) }); });
需要说明一下的是,newland.js分成两大块,app目录下的模块是用户自建的文件或系统指令下建立的模块(下称app模块),system目录下的模块则是辅助app模块运行用的。需要用户动手的app模块越简单就越好!任何复杂的类最好不要用,就算用都交由框架去处理,去new 实例;任何复杂的功能都做用钩子的形式,或通俗地说,是回调函数的形式,交由框架去调度;所有不确定的功能都做成自定义事件形式,由框架或框架的另外加载的模块去fire。诸如此类,所有耦合就松绑了,得以最大限度地适应需求的变化。