整个ExtJS框架都是以一种面向对象的方式开发的,在自己编写的程序也可以应用面向对象的设计手法。
1. 在现有类基础上创建子类
应用 Ext.extend() 能够建立强大的面向对象的JavaScript类层次结构。你可以用它构建你自己的类和派生类,当然也可以扩展Ext的内建类。 以项目中的 divo.form.AddOrEditForm 为例,它是对 Ext.form.FormPanel 的扩展,是所有新建和修改表单的基类:
/**
* 新建或修改表单(基类)
*/
divo.form.AddOrEditForm = Ext.extend(Ext.form.FormPanel, {
recordId : null, //public 属性
adding : false,
。。。
//类的构造器
initComponent : function() {
。。。
divo.form.AddOrEditForm.superclass.initComponent.call(this);
},
//public 方法
initForm : function(t) {
。。
},
//扩展父类方法
afterRender : function() {
divo.form.AddOrEditForm.superclass.afterRender.call(this)
。。。
},
//模板方法(子类可以重写)
validateBeforeSave : function(item) {
return true
}
。。。
})
我们通过扩展 divo.form.AddOrEditForm 创建具体的表单,例如:
/**
* 用户新建或修改表单
*/
form.admin.UserAddOrEditForm = Ext.extend(divo.form.AddOrEditForm, {
initComponent : function() {
Ext.apply(this, {
initFocusFldName : 'full_name', //让基类的 public 属性变为 private 属性
url : "/users",
items : [{
fieldLabel : divo.required + "姓名",
name : "full_name",
allowBlank : false,
width : 100
。。。
})
form.admin.UserAddOrEditForm.superclass.initComponent.call(this);
},
//重写模板方法
validateBeforeSave : function(item) {
var t = /^[a-zA-Z0-9_\-]+$/;
if (!t.test(item.name)) {
this.say("用户名中含有非法字符!")
return false
}
return true
},
。。。
})
Ext.reg("form.admin.UserAddOrEditForm", form.admin.UserAddOrEditForm)
// EOP
可以这样使用这个表单:
/**
* 用户新建或修改窗口
*/
window.admin.UserAddOrEditWindow = Ext.extend(Ext.Window, {
adding : null,
recordId : null,
closeAction : "hide",
cancelAction : "hide",
initComponent : function() {
Ext.apply(this, {
title : (this.adding?'新建':'修改')+'用户',
height : 300,
width : 400,
modal : true,
layout : "fit",
items : {
id : this.myId('form'),
xtype : "form.admin.UserAddOrEditForm",
adding : this.adding, //public 属性赋值
recordId : this.recordId
},
})
window.admin.UserAddOrEditWindow.superclass.initComponent.call(this)
},
。。。
})
//EOP
通过以上步骤,我们构造出了这样的类层次结构:
Ext.form.FormPanel
+-- divo.form.AddOrEditForm
+-- form.admin.UserAddOrEditForm
2. 标记 public 方法
器件类的 public 方法会被其他器件使用,因此不能随意更改其名字和接口,要求按下面的代码例子那样标记 public 方法:
//public
addItem : function() {
。。。
},
3. 应用模板方法设计模式
GOF给出的模板方法(Template Method)模式的定义是这样的:
模板方法是一个操作中的算法的骨架,而将一些步骤延迟到子类中。使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
这里的算法的结构,可以理解为你根据需求设计出来的业务流程。特定的步骤就是指那些可能在内容上存在变数的环节。可以看出来,
模板方法模式也是为了巧妙解决变化对系统带来的影响而设计的。使用模板方法使系统扩展性增强,最小化了变化对系统的影响。
请看上面的代码例子,validateBeforeSave 就是一个模板方法,它在基类中这样被使用:
// public
save : function(callbackOnSuccess) {
var item = this.getForm().getObjectValues()
if (!this.validateBeforeSave(item)) {
return
}
Ext.MessageBox.wait("正在保存...", '请稍候');
。。。
它的巧妙之处在于,如果子类不重写该方法,它的默认实现就是什么也不验证(直接返回真值)。如果子类需要添加验证业务逻辑,则可以 重写它,如上面的 UserAddOrEditForm 代码例子所展示的那样。
子类中重写模板方法时,要求像这样做上标记:
//重写
validateBeforeSave : function(item) {
。。。
},
4. 往 Ext.Component 中加入新方法
在《用ExtJS开发客户端 3 跨器件的消息发布和订阅》中,你可以看到下面这样的代码:
grid.xf.CorpUserGrid = Ext.extend(divo.grid.SmartSimpleGrid, {
initComponent : function() {
...
this.subscribe("window.xf.AdvisorChanged",this.onCorpInfoChanged,this)
this.on('beforedestroy',function() {
this.unsubscribe()
},this)
grid.xf.CorpUserGrid.superclass.initComponent.apply(this,arguments)
},
onCorpInfoChanged : function(subj, msg, data) {
this.onRefreshList()
},
...
查阅 ExtJS的API文档,你肯定会发现它是没有subscribe、unsubscribe这两个方法的,这是通过下面这样的代码往 Ext.Component 中加入的新方法 (参见client/js/core/ExtOverride.js):
Ext.override(Ext.Component, {
。。。
subscribe : function(name, callback, scope) {
if (!this.pagebusSubs)
this.pagebusSubs = []
this.pagebusSubs.push(divo.subscribe(name, callback, scope))
},
unsubscribe : function() {
if (!this.pagebusSubs) return
for (var i = 0; i < this.pagebusSubs.length; i++)
divo.unsubscribe(this.pagebusSubs[i])
},
publish : function(name, message) {
divo.publish(name, message)
},
。。。
因为大多数Ext界面器件继承自Ext.Component,所以可以在器件类中直接用 this 引用这些方法。
5. 参考文献
- 深入浅出Java模式设计之模板方法模式