上一篇博客简单介绍了该系统的元数据模型,这周继续为大家介绍SIGNAVIO流程设计器这一开源系统部分功能的核心代码。
事件驱动原型
首先看整个系统的事件原型。和其他核心操作一样,事件相关接口也封装在ORYX.Editor对象中,包括注册、取消注册、执行、暂停事件、激活事件等接口,由于JS语言本身对函数式编程的良好支持,所以系统事件原型实现起来就很容易了。
系统事件响应类型主要包括:
1、动作响应事件,如鼠标点击事件、键盘操作事件,这一事件主要由HTML DOCUMENT实现,设计器只需要针对自身业务动态添加事件监听就可以了。
2、功能响应事件,整个设计器系统基于这一事件模型实现,如工具栏命令执行事件、节点元素拖拽事件、菜单事件等。
所有事件类型的常量定义如以下代码所示:
//动作响应事件、主要由HTML DOCUMENT实现
ORYX.CONFIG.EVENT_MOUSEDOWN = "mousedown";
ORYX.CONFIG.EVENT_MOUSEUP = "mouseup";
ORYX.CONFIG.EVENT_MOUSEOVER = "mouseover";
ORYX.CONFIG.EVENT_MOUSEOUT = "mouseout";
ORYX.CONFIG.EVENT_MOUSEMOVE = "mousemove";
ORYX.CONFIG.EVENT_DBLCLICK = "dblclick";
ORYX.CONFIG.EVENT_KEYDOWN = "keydown";
ORYX.CONFIG.EVENT_KEYUP = "keyup";
ORYX.CONFIG.EVENT_LOADED = "editorloaded";
//功能响应事件
ORYX.CONFIG.EVENT_EXECUTE_COMMANDS = "executeCommands";
ORYX.CONFIG.EVENT_STENCIL_SET_LOADED = "stencilSetLoaded";
ORYX.CONFIG.EVENT_SELECTION_CHANGED = "selectionchanged";
ORYX.CONFIG.EVENT_SHAPEADDED = "shapeadded";
ORYX.CONFIG.EVENT_SHAPEREMOVED = "shaperemoved";
ORYX.CONFIG.EVENT_PROPERTY_CHANGED = "propertyChanged";
ORYX.CONFIG.EVENT_DRAGDROP_START = "dragdrop.start";
ORYX.CONFIG.EVENT_SHAPE_MENU_CLOSE = "shape.menu.close";
ORYX.CONFIG.EVENT_DRAGDROP_END = "dragdrop.end";
ORYX.CONFIG.EVENT_RESIZE_START = "resize.start";
ORYX.CONFIG.EVENT_RESIZE_END = "resize.end";
ORYX.CONFIG.EVENT_DRAGDOCKER_DOCKED = "dragDocker.docked";
ORYX.CONFIG.EVENT_HIGHLIGHT_SHOW = "highlight.showHighlight";
ORYX.CONFIG.EVENT_HIGHLIGHT_HIDE = "highlight.hideHighlight";
ORYX.CONFIG.EVENT_LOADING_ENABLE = "loading.enable";
ORYX.CONFIG.EVENT_LOADING_DISABLE = "loading.disable";
ORYX.CONFIG.EVENT_LOADING_STATUS = "loading.status";
ORYX.CONFIG.EVENT_OVERLAY_SHOW = "overlay.show";
ORYX.CONFIG.EVENT_OVERLAY_HIDE = "overlay.hide";
ORYX.CONFIG.EVENT_ARRANGEMENT_TOP = "arrangement.setToTop";
ORYX.CONFIG.EVENT_ARRANGEMENT_BACK = "arrangement.setToBack";
ORYX.CONFIG.EVENT_ARRANGEMENT_FORWARD = "arrangement.setForward";
ORYX.CONFIG.EVENT_ARRANGEMENT_BACKWARD = "arrangement.setBackward";
ORYX.CONFIG.EVENT_PROPWINDOW_PROP_CHANGED = "propertyWindow.propertyChanged";
ORYX.CONFIG.EVENT_LAYOUT_ROWS = "layout.rows";
ORYX.CONFIG.EVENT_LAYOUT_BPEL = "layout.BPEL";
ORYX.CONFIG.EVENT_LAYOUT_BPEL_VERTICAL = "layout.BPEL.vertical";
ORYX.CONFIG.EVENT_LAYOUT_BPEL_HORIZONTAL = "layout.BPEL.horizontal";
ORYX.CONFIG.EVENT_LAYOUT_BPEL_SINGLECHILD = "layout.BPEL.singlechild";
ORYX.CONFIG.EVENT_LAYOUT_BPEL_AUTORESIZE = "layout.BPEL.autoresize";
ORYX.CONFIG.EVENT_AUTOLAYOUT_LAYOUT = "autolayout.layout";
ORYX.CONFIG.EVENT_UNDO_EXECUTE = "undo.execute";
ORYX.CONFIG.EVENT_UNDO_ROLLBACK = "undo.rollback";
ORYX.CONFIG.EVENT_BUTTON_UPDATE = "toolbar.button.update";
ORYX.CONFIG.EVENT_LAYOUT = "layout.dolayout";
ORYX.CONFIG.EVENT_GLOSSARY_LINK_EDIT = "glossary.link.edit";
ORYX.CONFIG.EVENT_GLOSSARY_SHOW = "glossary.show.info";
ORYX.CONFIG.EVENT_GLOSSARY_NEW = "glossary.show.new";
ORYX.CONFIG.EVENT_DOCKERDRAG = "dragTheDocker";
以上代码位于oryx.debug.js文件中。
事件操作相关代码如下所示:
disableEvent: function(eventType){
if(eventType == ORYX.CONFIG.EVENT_KEYDOWN) {
this._keydownEnabled = false;
}
if(eventType == ORYX.CONFIG.EVENT_KEYUP) {
this._keyupEnabled = false;
}
if(this.DOMEventListeners.keys().member(eventType)) {
var value = this.DOMEventListeners.remove(eventType);
this.DOMEventListeners['disable_' + eventType] = value;
}
},
enableEvent: function(eventType){
if(eventType == ORYX.CONFIG.EVENT_KEYDOWN) {
this._keydownEnabled = true;
}
if(eventType == ORYX.CONFIG.EVENT_KEYUP) {
this._keyupEnabled = true;
}
if(this.DOMEventListeners.keys().member("disable_" + eventType)) {
var value = this.DOMEventListeners.remove("disable_" + eventType);
this.DOMEventListeners[eventType] = value;
}
},
/**
* Methods for the PluginFacade
*/
registerOnEvent: function(eventType, callback) {
if(!(this.DOMEventListeners.keys().member(eventType))) {
this.DOMEventListeners[eventType] = [];
}
this.DOMEventListeners[eventType].push(callback);
},
unregisterOnEvent: function(eventType, callback) {
if(this.DOMEventListeners.keys().member(eventType)) {
this.DOMEventListeners[eventType] = this.DOMEventListeners[eventType].without(callback);
} else {
// Event is not supported
// TODO: Error Handling
}
},
事件执行:
/**
* Helper method to execute an event immediately. The event is not
* scheduled in the _eventsQueue. Needed to handle Layout-Callbacks.
*/
_executeEventImmediately: function(eventObj) {
if(this.DOMEventListeners.keys().member(eventObj.event.type)) {
this.DOMEventListeners[eventObj.event.type].each((function(value) {
value(eventObj.event, eventObj.arg);
}).bind(this));
}
},
_executeEvents: function() {
this._queueRunning = true;
while(this._eventsQueue.length > 0) {
var val = this._eventsQueue.shift();
this._executeEventImmediately(val);
}
this._queueRunning = false;
},
/**
* Leitet die Events an die Editor-Spezifischen Event-Methoden weiter
* @param {Object} event Event , welches gefeuert wurde
* @param {Object} uiObj Target-UiObj
*/
handleEvents: function(event, uiObj) {
ORYX.Log.trace("Dispatching event type %0 on %1", event.type, uiObj);
switch(event.type) {
case ORYX.CONFIG.EVENT_MOUSEDOWN:
this._handleMouseDown(event, uiObj);
break;
case ORYX.CONFIG.EVENT_MOUSEMOVE:
this._handleMouseMove(event, uiObj);
break;
case ORYX.CONFIG.EVENT_MOUSEUP:
this._handleMouseUp(event, uiObj);
break;
case ORYX.CONFIG.EVENT_MOUSEOVER:
this._handleMouseHover(event, uiObj);
break;
case ORYX.CONFIG.EVENT_MOUSEOUT:
this._handleMouseOut(event, uiObj);
break;
}
/* Force execution if necessary. Used while handle Layout-Callbacks. */
if(event.forceExecution) {
this._executeEventImmediately({event: event, arg: uiObj});
} else {
this._eventsQueue.push({event: event, arg: uiObj});
}
if(!this._queueRunning) {
this._executeEvents();
}
// TODO: Make this return whether no listener returned false.
// So that, when one considers bubbling undesireable, it won't happen.
return false;
},
事件初始化:
_initEventListener: function(){
// Register on Events
document.documentElement.addEventListener(ORYX.CONFIG.EVENT_KEYDOWN, this.catchKeyDownEvents.bind(this), false);
document.documentElement.addEventListener(ORYX.CONFIG.EVENT_KEYUP, this.catchKeyUpEvents.bind(this), false);
// Enable Key up and down Event
this._keydownEnabled = true;
this._keyupEnabled = true;
this.DOMEventListeners[ORYX.CONFIG.EVENT_MOUSEDOWN] = [];
this.DOMEventListeners[ORYX.CONFIG.EVENT_MOUSEUP] = [];
this.DOMEventListeners[ORYX.CONFIG.EVENT_MOUSEOVER] = [];
this.DOMEventListeners[ORYX.CONFIG.EVENT_MOUSEOUT] = [];
this.DOMEventListeners[ORYX.CONFIG.EVENT_SELECTION_CHANGED] = [];
this.DOMEventListeners[ORYX.CONFIG.EVENT_MOUSEMOVE] = [];
},
基于以上事件模型,系统很多复杂功能就可以顺利实现了。
比如节点元素选中开始拖拽时需要隐藏节点元素所支持的规则菜单项,当节点元素终止拖拽时需要默认选中且展示节点元素所支持的规则菜单项。
熟悉该系统的读者应该知道这一功能。其核心代码如下所示:
事件注册代码:
ORYX.Plugins.ShapeMenuPlugin = {
construct: function(facade) {
this.facade = facade;
this.alignGroups = new Hash();
var containerNode = this.facade.getCanvas().getHTMLContainer();
this.shapeMenu = new ORYX.Plugins.ShapeMenu(containerNode);
this.currentShapes = [];
// Register on dragging and resizing events for show/hide of ShapeMenu
this.facade.registerOnEvent(ORYX.CONFIG.EVENT_DRAGDROP_START, this.hideShapeMenu.bind(this));
this.facade.registerOnEvent(ORYX.CONFIG.EVENT_DRAGDROP_END, this.showShapeMenu.bind(this));
事件方法相关代码:
hideShapeMenu: function(event) {
window.clearTimeout(this.timer);
this.timer = null;
this.shapeMenu.hide();
},
showShapeMenu: function( dontGenerateNew ) {
if( !dontGenerateNew || this.resetElements ){
window.clearTimeout(this.timer);
this.timer = window.setTimeout(function(){
// Close all Buttons
this.shapeMenu.closeAllButtons();
// Show the Morph Button
this.showMorphButton(this.currentShapes);
// Show the Stencil Buttons
this.showStencilButtons(this.currentShapes);
// Show the ShapeMenu
this.shapeMenu.show(this.currentShapes);
this.resetElements = false;
}.bind(this), 300)
} else {
window.clearTimeout(this.timer);
this.timer = null;
// Show the ShapeMenu
this.shapeMenu.show(this.currentShapes);
}
},
在比如线条添加DragDocker的时候,如果线条上已经存在Docker,此时如果鼠标移动到细条上时需要动态显示已经添加的Docker,核心代码如下所示:
事件注册代码:
ORYX.Plugins.DragDocker = Clazz.extend({
/**
* Constructor
* @param {Object} Facade: The Facade of the Editor
*/
construct: function(facade) {
this.facade = facade;
// Set the valid and invalid color
this.VALIDCOLOR = ORYX.CONFIG.SELECTION_VALID_COLOR;
this.INVALIDCOLOR = ORYX.CONFIG.SELECTION_INVALID_COLOR;
// Define Variables
this.shapeSelection = undefined;
this.docker = undefined;
this.dockerParent = undefined;
this.dockerSource = undefined;
this.dockerTarget = undefined;
this.lastUIObj = undefined;
this.isStartDocker = undefined;
this.isEndDocker = undefined;
this.undockTreshold = 10;
this.initialDockerPosition = undefined;
this.outerDockerNotMoved = undefined;
this.isValid = false;
// For the Drag and Drop
// Register on MouseDown-Event on a Docker
this.facade.registerOnEvent(ORYX.CONFIG.EVENT_MOUSEDOWN, this.handleMouseDown.bind(this));
this.facade.registerOnEvent(ORYX.CONFIG.EVENT_DOCKERDRAG, this.handleDockerDrag.bind(this));
// Register on over/out to show / hide a docker
this.facade.registerOnEvent(ORYX.CONFIG.EVENT_MOUSEOVER, this.handleMouseOver.bind(this));
this.facade.registerOnEvent(ORYX.CONFIG.EVENT_MOUSEOUT, this.handleMouseOut.bind(this));
},
事件方法实现代码:
/**
* MouseOver Handler
*
*/
handleMouseOver: function(event, uiObj) {
// If there is a Docker, show this
if(!this.docker && uiObj instanceof ORYX.Core.Controls.Docker) {
uiObj.show()
} else if(!this.docker && uiObj instanceof ORYX.Core.Edge) {
uiObj.dockers.each(function(docker){
docker.show();
})
}
},
OOP思想
为了使系统更具有灵活性和可扩展性,SIGNAVIO整体框架也采用的OOP思想进行编程,OOP相关知识、以及JS相关基础知识都不再赘述(在JAVA语言和BS架构大行其道的今天,一个不懂得OOP和JS的程序员是无法想象的)。类的继承基于JS的prototype实现。
实现的核心代码如下所示(包括代码的英文注释):
/**
* The super class for all classes in ORYX. Adds some OOP feeling to javascript.
* See article "Object Oriented Super Class Method Calling with JavaScript" on
* http://truecode.blogspot.com/2006/08/object-oriented-super-class-method.html
* for a documentation on this. Fairly good article that points out errors in
* Douglas Crockford's inheritance and super method calling approach.
* Worth reading.
* @class Clazz
*/
var Clazz = function() {};
/**
* Empty constructor.
* @methodOf Clazz.prototype
*/
Clazz.prototype.construct = function() {};
/**
* Can be used to build up inheritances of classes.
* @example
* var MyClass = Clazz.extend({
* construct: function(myParam){
* // Do sth.
* }
* });
* var MySubClass = MyClass.extend({
* construct: function(myParam){
* // Use this to call constructor of super class
* arguments.callee.$.construct.apply(this, arguments);
* // Do sth.
* }
* });
* @param {Object} def The definition of the new class.
*/
Clazz.extend = function(def) {
var classDef = function() {
if (arguments[0] !== Clazz) { this.construct.apply(this, arguments); }
};
var proto = new this(Clazz);
var superClass = this.prototype;
for (var n in def) {
var item = def[n];
if (item instanceof Function) item.$ = superClass;
proto[n] = item;
}
classDef.prototype = proto;
//Give this new class the same static extend method
classDef.extend = this.extend;
return classDef;
};
以上代码相当于提供了一个实现继承功能的模板,采用以上模板定义的对象,都具有继承功能。
最核心代码解释:
1、classDef.extend = this.extend 功能:当采用该模板定义一个对象时,将该继承模板也赋予这一个对象,从而使得该对象具有定义子对象的能力,从而实现真正意义上的继承。
2、this.construct.apply(this, arguments)这一样代码的功能是:当采用该模板定义一个对象时,调用对应对象的构造函数construct,(construct函数必须在对象内部显示的定义,大家有没有想到OO里面的依赖倒置原则呢?);
3、classDef.prototype = proto;继承属性。
花自飘零水自流,模板定义好了,调用就很容易了!
看看系统这一行代码:
ORYX.Core.UIObject = Clazz.extend(ORYX.Core.UIObject);
Clazz.extend正是我们刚刚定义的继承模板方法。我么也说了采用模板方法定义的对象内部必须显示定义有construct构造函数,所以得验证一下,抽取ORYX.Core.UIObject原型定义:
ORYX.Core.UIObject原型定义的部分代码:
ORYX.Core.UIObject = {
/**
* Constructor of the UIObject class.
*/
construct: function(options) {
this.isChanged = true; //Flag, if UIObject has been changed since last update.
this.isResized = true;
this.isVisible = true; //Flag, if UIObject's display attribute is set to 'inherit' or 'none'
this.isSelectable = false; //Flag, if UIObject is selectable.
this.isResizable = false; //Flag, if UIObject is resizable.
this.isMovable = false; //Flag, if UIObject is movable.
this.id = ORYX.Editor.provideId(); //get unique id
this.parent = undefined; //parent is defined, if this object is added to another uiObject.
this.node = undefined; //this is a reference to the SVG representation, either locally or in DOM.
this.children = []; //array for all add uiObjects
this.bounds = new ORYX.Core.Bounds(); //bounds with undefined values
this._changedCallback = this._changed.bind(this); //callback reference for calling _changed
this.bounds.registerCallback(this._changedCallback); //set callback in bounds
if(options && options.eventHandlerCallback) {
this.eventHandlerCallback = options.eventHandlerCallback;
}
},
看到没? construct出来了! 虽然ORYX.Core.UIObject是系统自定义的一个JS对象,但是最后是采用继承模板实现,所以现在的ORYX.Core.UIObject对象应该是一个有个extend功能的父类对象,可以扩展子类对象。
那到底是不是呢?
我们在系统里面找到ORYX.Core.AbstractShape这个类,看其定义:
ORYX.Core.AbstractShape部分定义代码:
ORYX.Core.AbstractShape = ORYX.Core.UIObject.extend(
/** @lends ORYX.Core.AbstractShape.prototype */
{
/**
* Constructor
*/
construct: function(options, stencil) {
arguments.callee.$.construct.apply(this, arguments);
this.resourceId = ORYX.Editor.provideId(); //Id of resource in DOM
// stencil reference
this._stencil = stencil;
// if the stencil defines a super stencil that should be used for its instances, set it.
if (this._stencil._jsonStencil.superId){
stencilId = this._stencil.id()
superStencilId = stencilId.substring(0, stencilId.indexOf("#") + 1) + stencil._jsonStencil.superId;
stencilSet = this._stencil.stencilSet();
this._stencil = stencilSet.stencil(superStencilId);
}
//Hash map for all properties. Only stores the values of the properties.
this.properties = new Hash();
this.propertiesChanged = new Hash();
// List of properties which are not included in the stencilset,
// but which gets (de)serialized
this.hiddenProperties = new Hash();
//Initialization of property map and initial value.
this._stencil.properties().each((function(property) {
var key = property.prefix() + "-" + property.id();
this.properties[key] = property.value();
this.propertiesChanged[key] = true;
}).bind(this));
// if super stencil was defined, also regard stencil's properties:
if (stencil._jsonStencil.superId) {
stencil.properties().each((function(property) {
var key = property.prefix() + "-" + property.id();
var value = property.value();
var oldValue = this.properties[key];
this.properties[key] = value;
this.propertiesChanged[key] = true;
// Raise an event, to show that the property has changed
// required for plugins like processLink.js
//window.setTimeout( function(){
this._delegateEvent({
type : ORYX.CONFIG.EVENT_PROPERTY_CHANGED,
name : key,
value : value,
oldValue: oldValue
});
//}.bind(this), 10)
}).bind(this));
}
},
谜底彻底揭晓了,我们可以发现 ORYX.Core.AbstractShape的定义不再是简单的JS语法“{}”,而是通过ORYX.Core.UIObject对象的extend方法,我们也可以发现 ORYX.Core.AbstractShape的构造函数中多了这么一行代码 “arguments.callee.$.construct.apply(this, arguments)” ,聪明的你一定猜到了这是在调用ORYX.Core.UIObjec对象的构造函数。由于ORYX.Core.UIObject最终是采用的继承模板实现的对象,有了extend功能,同宗原理,ORYX.Core.AbstractShape也有了extend功能,这样一来,整个系统继承体系雏形也就出现了,就是SIGNAVIO现在的面向对象体系结构。这样一来系统的开发就不在是过程式函数式了,系统也更具有灵活性,可扩展性和可维护性。
====================================================================
声明:本文首发iteye blog,转载请注明作者信息及原文地址,谢谢
作者信息:
马恩亮(elma@wisedu.com)
=====================================================================
相关推荐
冒号课堂-编程范式与OOP思想 mobi kindle
Java面向对象编程(Object-Oriented Programming,简称OOP)是一种强大的编程范式,它将复杂的程序设计问题通过模拟现实世界中的对象来解决。在Java中,OOP主要包含四个核心概念:封装、继承、多态和抽象。下面将...
面向对象编程(OOP)是现代软件开发中最常用的编程范式之一。它的核心思想是将数据和操作这些数据的方法封装在对象中,对象是类的实例,而类是对象的蓝图。OOP的四个基本原则是封装、继承、多态和抽象。 封装是保护...
《冒号课堂:编程范式与OOP思想》是一本深入探讨编程理论和技术的书籍,主要聚焦于编程范式和面向对象编程(Object-Oriented Programming, OOP)的概念。编程范式是编写程序的不同方式,它定义了如何组织代码、处理...
在Python编程语言中,面向对象编程(Object-Oriented Programming,OOP)是一种重要的编程范式,它基于“对象”的概念。在这个模型中,数据和操作数据的方法被封装在一起,形成了一个独立的实体,即对象。这个教程将...
飞悦580·制作 <br>解压密码:www.fy580.cn<br><br>北大青鸟ACCP5.0二期 --- <br>深入.NET平台和C#编程 --- <br>项目实战 --- 《影院售票系统》 <br> <br>ShowList.xml --- XML文件 <br>影院...
### 编程范式与OOP思想 #### 一、编程范式的概念 编程范式是一种编程风格,它定义了一套编程的基本结构和常见的做法,是程序员用来组织代码的一种思维方式。不同的编程语言支持不同的编程范式,每种编程范式都有其...
Java面向对象编程(Object-Oriented Programming,简称OOP)是Java编程的核心,它是一种将现实世界中的事物抽象为程序中的对象的编程方法。在Java OOP中,我们通过类(Class)、对象(Object)、封装(Encapsulation...
Java 第一阶段建立编程思想 【零钱通(OOP)】---- 代码 Java 第一阶段建立编程思想 【零钱通(OOP)】---- 代码 Java 第一阶段建立编程思想 【零钱通(OOP)】---- 代码 Java 第一阶段建立编程思想 【零钱通(OOP)...
内容概要:通过带着读者编写Java基础语法测试代码,理解基础数据类型-注解-数组-inet-反射-oop-Java代码实现过程和测试代码 适合人群:初学java语法入门测试代码 能学到什么:①注解、数组、数据类型、inet、method...
内置四个小的实验, 由初级逐步提升难度, 适合刚入门的小白, 作为System Verilog的学习值得推荐, 动手能力迅速提升。 注明: 本实验所用软件Questa Sim,配套使用,可以找我要相关软件的安装包。
"homework-of-oop-course-design-master.zip" 这个压缩包文件,正是针对Java课程设计的一份宝贵资源,包含了Java学习资料、项目源码及教程,旨在帮助学习者深化对Java的理解,提高编程技能。 面向对象编程(Object-...
笔记
在Android架构设计中,面向对象编程(Object-Oriented Programming, OOP)是核心基础,尤其是在构建可维护、可扩展的软件系统时。本主题"android架构-复习基本OOP知识d"旨在帮助开发者深入理解并熟练运用OOP原则,...
面向对象软件开发技术(OOA-OOD-OOP)
Java面向对象编程基础 在计算机科学中,面向对象编程(OOP)是一种广泛采用的编程范式,它基于“对象”的概念,强调数据和操作数据的方法...阅读提供的"04-Java-OOP-Basics.pdf"文档,将有助于深化对这些概念的理解。
《C#影院售票系统——基于OOP思想的实现与解析》 C#语言在软件开发领域有着广泛的应用,尤其在创建高效、稳定的桌面应用程序方面表现出色。本篇将深入探讨一个基于C#实现的影院售票系统,该系统充分体现了面向对象...