大家好!今天跟大家一起分享我的OOP框架YOOP重构实践,希望能与大家一起共同学习、共同进步。
本文展示了我对没有编写测试的YOOP原始版本的重构过程。通过该重构,力图将一个杂乱无章的遗留代码重构为有良好测试的、结构良好、可读性较强的高质量代码。
在本次重构中,我不但会对代码结构进行重构,还会改变部分行为(如将“抽象类要检查是否实现了接口成员和父类的抽象方法”修改为“抽象类不检查是否实现了接口成员和父类的抽象方法”;将“抽象类、类只能继承1个接口”修改为“可以继承多个接口”等等)。改变行为时,必须先添加或者修改测试,然后才能小步地改变行为。
原始版本
(function () { /************************************************** String对象扩展 *********************************************************** 扩展方法: contain containIgnoreCase trim */ if (!String.prototype.contain) { String.prototype.contain = function (str) { /* 使用RegExp对象来构造动态匹配。 注意!str是字符串,因此需要转义! 由于JavaScript字符串中的“\”是一个转义字符,因此,使用显式构造函数创建RegExp实例对象时,应将原始正则表达式中的“\”用“\\”替换。例如,在代码1.2中的两条语句是等价的。 代码1.2 转义字符中的“\”:1.2.htm <script language="javascript"> var re1 = new RegExp("\\d{5}"); var re2 = /\d{5}/; alert("re1="+re1+"\nre2="+re2); </script> 由于正则表达式模式文本中的转义字符也是“\”,如果正则表达式中要匹配原义字符“\”,在正则表达式模式文本中要以“\\”来表示,当使用显式构造函数的方式创建RegExp实例对象的时候,就需要使用“\\\\”来表示原义字符“\”。 var re = new RegExp(\\\\)。 */ var reg = new RegExp(str); if (this.match(reg)) { //用this指针指代本体 return true; } else { return false; } } } /*****************************************************************************************************************************/ //当前是否处于创建类的阶段。 //放在自执行函数中,initializing就是自执行函数的内部变量,自执行函数的上下文结束后,外部就不能访问initializing了。 //不用var的话,就不是当前上下文的一个变量了,而是全局对象的一个属性。这样外部就能够访问了。 var initializing = false; // var count = 0; //获得函数的参数数组 function argumentNames(fn) { var names = fn.toString().match(/^[\s\(]*function[^(]*\(([^\)]*)\)/)[1].replace(/\s+/g, '').split(','); return names.length == 1 && !names[0] ? [] : names; }; /* 深拷贝 */ function extendDeep(parent, child) { var i, toStr = Object.prototype.toString, sArr = "[object Array]", sOb = "[object Object]", type = "", child = child || {}; for (i in parent) { //if (parent.hasOwnProperty && parent.hasOwnProperty(i)) { // if (typeof parent[i] === 'object') { //null === 'object'也为true! type = toStr.call(parent[i]); if (type === sArr || type === sOb) { //如果为数组或object对象 child[i] = type === sArr ? [] : {}; extendDeep(parent[i], child[i]); } else { child[i] = parent[i]; } } //} return child; }; //获得函数名 function getFunctionName(fn) { var name = ""; if (!fn) { return null; } name = fn.toString().match(/^.*function\s*([^\(]*)/); return name === null ? name : name[1]; }; //判断是否为数组 function isArray(val) { return Object.prototype.toString.call(val) === "[object Array]"; }; //检查抽象类的公有方法+虚方法+抽象方法是否包含父类的抽象方法/属性 或 接口方法/属性。 //不用hasOwnProperty判断!否则就检查不到是否包含了父类的抽象方法/属性 或 接口方法/属性。 function check(parentClass, interface, children) { // if (!parent || !interface || !children) { // throw new Error("check - arguments error!"); // } var name = ""; if (parentClass) { //检查是否实现了抽象方法/属性 for (name in parentClass.prototype) { if (parentClass.prototype.hasOwnProperty(name)) { // console.log(name); if (name === "constructor") { continue; } if (name.contain("Abstract_")) { //抽象方法 if (typeof parentClass.prototype[name] === "function") { if (children[name.slice(9)] === undefined || typeof children[name.slice(9)] !== "function") { // var t = name.slice(9); throw new Error("Abstract method '" + name + "' must be overwrited!"); } } //抽象属性 else { if (children[name.slice(9)] === undefined || typeof children[name.slice(9)] === "function") { // var t = name.slice(9); // var p = children[name.slice(9)]; // var q = typeof children[name.slice(9)]; throw new Error("Abstract attribute '" + name + "' must be overwrited!"); } } } } } } if (!interface) { return; } //检查是否实现了接口方法/属性 for (name in interface.prototype) { // console.log(name); if (name === "constructor") { continue; } // if (interface.prototype.hasOwnProperty(name)) { //接口方法 if (typeof interface.prototype[name] === "function") { // var t = name.slice(10); // var m = children[name.slice(10)]; // console.log("t = " + t); // console.log("m = " + m); if (children[name.slice(10)] === undefined || typeof children[name.slice(10)] !== "function") { throw new Error("Interface method '" + name + "' must be overwrited!"); } } //接口属性 else { // var t = name.slice(10); // var m = children[name.slice(10)]; // console.log("t = " + t); // console.log("m = " + m); if (children[name.slice(10)] === undefined || typeof children[name.slice(10)] === "function") { throw new Error("Interface attribute '" + name + "' must be overwrited!"); } } // } } }; //检查抽象成员 function addAbstract(abstract, currentClass, temp) { var name = ""; for (name in abstract) { if (abstract.hasOwnProperty(name)) { // if (typeof abstract[name] !== "function") { // throw new Error("Virtual attribute is not allowed!"); // } // else { //抽象方法前面加"Abstract_"前缀 currentClass.prototype["Abstract_" + name] = abstract[name]; // currentClass.prototype[name] = abstract[name]; temp[name] = abstract[name]; //加入temp // } } } }; //检查虚方法(不能为虚属性) function addVirtual(virtual, currentClass, temp) { var name = ""; for (name in virtual) { if (virtual.hasOwnProperty(name)) { if (typeof virtual[name] !== "function") { throw new Error("Virtual attribute is not allowed!"); } else { currentClass.prototype[name] = virtual[name]; temp[name] = virtual[name]; //加入temp } } } }; //加入密封方法。 //没有实现检查子类是否重写了父类的密封方法,只是定义了一个规范。 function addSealed(sealed, currentClass, temp) { var name = ""; for (name in sealed) { if (sealed.hasOwnProperty(name)) { currentClass.prototype[name] = sealed[name]; temp[name] = sealed[name]; //加入temp } } }; //获得在原型prototype中不存在同名的str。 //如果有同名,则加上前缀"_" function getNoRepeatStrInPrototype(prototype, str) { var new_str = ""; if (!prototype[str]) { return str; } new_str = "_" + str; return getNoRepeatStrInPrototype(prototype, new_str); } //创建接口 //接口可以继承接口 function MyInterface(_parent, _method, _attribute) { var i = 0, args = null; var parent = null, method = null, attribute = null; if (typeof _parent === "function") { if (getFunctionName(_parent) !== "I") { throw new Error("Interface must inherit interface!"); } else { parent = _parent; //形如“MyInterface(Parent, "A", "B", "GetName");” if (_method && !isArray(_method)) { method = Array.prototype.slice.call(arguments, 1); attribute = null; } //形如“MyInterface(Parent, ["A", "B", "GetName"], ["a", "c"]);” else { method = _method; attribute = _attribute; } } // console.log(parent.toString()); } else { parent = null; //形如“MyInterface("A", "B", "GetName");” if (_method && !isArray(_method)) { method = arguments attribute = null; } //形如“MyInterface(["A", "B", "GetName"], ["a", "c"]);” else { method = arguments[0]; attribute = arguments[1]; } } function I() { } // 如果此接口需要从其它接口扩展 if (parent) { I.prototype = new parent(); I.prototype.constructor = I; } // console.log("method = " + method); // console.log("attribute = " + attribute); // //形如“MyInterface(["A", "B", "GetName"], ["a", "c"]);” // if (isArray(method)) { //方法 for (i = 0; i < method.length; i++) { //加上前缀“Interface_” I.prototype["Interface_" + method[i]] = function () { throw new Error("This method must be overwrited!"); }; } //属性 if (attribute) { if (!isArray(attribute)) { throw new Error("Attribute must be array!"); } else { for (i = 0; i < attribute.length; i++) { //加上前缀“Interface_” I.prototype["Interface_" + attribute[i]] = 0; } } } // } // //形如“MyInterface("A", "B", "GetName");” // else { // args = Array.prototype.slice.call(arguments, 1); // //方法 // for (i = 0; i < args.length; i++) { // I.prototype[args[i]] = function () { // throw new Error("This method must be overwrited!"); // }; // } // } return I; }; //创建抽象类 //抽象类能够继承接口、抽象类以及实体类,但此处约定抽象类只能继承接口和抽象类,不能继承实体类! //(这样方便判断抽象类是否包含全部的父类(接口/抽象类)成员) function MyAbstract(_parent, _prop) { var Static = null; var k = null, name = null, temp = {}, virtual = {}; // if (arguments.length > 1) { // throw new Error("AbstractClass can't inherit other classes!"); // } var abstractClass = null, interface = null, prop = null; //原型恢复标志,用于防止第一次创建实例时恢复原型 var mark_resume = false; //取出父类、接口 if (arguments.length === 1) { prop = arguments[0]; // parent = null; abstractClass = null; interface = null; } //_parent为{Class: xx, Interface: xx} else if (typeof _parent === "object") { if (!_parent.Class && !_parent.Interface) { throw new Error("Please add AbstractClass or Interface!"); } if (getFunctionName(_parent.Class) === "F" || getFunctionName(_parent.Interface) === "F") { throw new Error("AbstractClass here can't inherit parentClass which is created by MyClass function!"); } abstractClass = _parent.Class; interface = _parent.Interface; prop = _prop; } //_parent直接为xx,就表示父类为抽象类 else if (typeof _parent === "function") { if (getFunctionName(_parent) === "F") { throw new Error("AbstractClass here can't inherit parentClass which is created by MyClass function!"); } abstractClass = _parent; interface = null; prop = _prop; } else { throw new Error("arguments is not allowed!"); } Static = prop.Static ? prop.Static : null; // 本次调用所创建的类(构造函数) function A() { // // 如果抽象父类存在,则实例对象的baseClass指向父类的原型 // // 这就提供了在实例对象中调用父类方法的途径 // if (abstractClass) { // this.baseClass = abstractClass.prototype; // } ////防止第一次创建实例时恢复原型 //if (mark_resume) { // //还原原型 // extendDeep(A.prototype.backUp_prototype, A.prototype); //} //else { // mark_resume = true; //} } // 如果此接口需要从其它接口扩展 if (abstractClass) { // //删除父类的私有成员,保留本类的私有成员 // for (name in abstractClass.prototype) { // if (abstractClass.prototype.hasOwnProperty(name)) { // //私有成员以“_”开头,可能有多个“_”(多层继承) // if (!name.match(/^_+/)) { // // delete parentClass.prototype[name]; // A.prototype[name] = abstractClass.prototype[name]; // } // } // } //A.prototype = new abstractClass(); A.prototype = extendDeep(abstractClass.prototype); A.prototype.constructor = A; // 如果父类存在,则实例对象的baseClass指向父类的原型。 // 这就提供了在实例对象中调用父类方法的途径。 //baseClass的方法是指向abstractClass的,不是指向F(子类)的! A.prototype[getNoRepeatStrInPrototype(abstractClass.prototype, "baseClass")] = abstractClass.prototype; //A.prototype.baseClass = abstractClass.prototype; } //加入构造函数 //抽象类本身因为不能实例化,所以不调用构造函数。 //抽象类中的构造函数供子类构造函数中调用。 if (prop.Init) { if (abstractClass) { A.prototype.Init = function (name) { return function () { //此处不用创建闭包了!因为外面已经创建了闭包,name已经被保存了! this.base = function () { //这个写法也可以!为什么不用apply修正this也行??! //parentClass.prototype[name](); //此处的arguments为base方法传入的形参 //注意!要加上“return”,这样才能返回parentClass.prototype[name]的返回值 return abstractClass.prototype[name].apply(abstractClass.prototype, arguments); }; //指向子类,可以用于模版模式 this.baseToSubClass = abstractClass.prototype[name]; //执行fn并返回执行的结果 //此处的arguments为F.prototype[name]方法传入的形参。 return prop[name].apply(this, arguments); }; }("Init"); } else { A.prototype.Init = prop.Init; } } if (prop.Private) { //私有属性/方法直接覆盖 for (name in prop.Private) { if (prop.Private.hasOwnProperty(name)) { A.prototype[name] = prop.Private[name]; } } } if (prop.Public) { for (name in prop.Public) { if (prop.Public.hasOwnProperty(name)) { //检查抽象成员,抽象成员放到Public或Protected中 if (name === "Abstract") { addAbstract(prop["Public"][name], A, temp); continue; } //检查虚方法,虚方法放到Public或Protected中 if (name === "Virtual") { addVirtual(prop["Public"][name], A, temp); continue; } //密封的方法(不允许子类重写) if (name === "Sealed") { addSealed(prop["Public"][name], A, temp); continue; } if (abstractClass && typeof prop.Public[name] === "function" && typeof A.prototype[name] === "function") { A.prototype[name] = function (name) { return function () { //此处不用创建闭包了!因为外面已经创建了闭包,name已经被保存了! this.base = function () { //这个写法也可以!为什么不用apply修正this也行??! //parentClass.prototype[name](); //此处的arguments为base方法传入的形参 //注意!要加上“return”,这样才能返回parentClass.prototype[name]的返回值 return abstractClass.prototype[name].apply(abstractClass.prototype, arguments); }; //指向子类,可以用于模版模式 this.baseToSubClass = abstractClass.prototype[name]; //执行fn并返回执行的结果 //此处的arguments为F.prototype[name]方法传入的形参。 return prop.Public[name].apply(this, arguments); }; }(name); } else { A.prototype[name] = prop.Public[name]; } temp[name] = prop.Public[name]; //用于检查是否包含父类的抽象方法/属性 或 接口方法/属性 } } } //保护成员 if (prop.Protected) { for (name in prop.Protected) { if (prop.Protected.hasOwnProperty(name)) { //检查抽象成员,抽象成员放到Public或Protected中 if (name === "Abstract") { addAbstract(prop["Protected"][name], A, temp); continue; } //检查虚方法,虚方法放到Public或Protected中 if (name === "Virtual") { addVirtual(prop["Protected"][name], A, temp); continue; } //密封的方法(不允许子类重写) if (name === "Sealed") { addSealed(prop["Protected"][name], A, temp); continue; } A.prototype[name] = prop.Protected[name]; } } } // //虚方法(不能为虚属性) // if (prop.Virtual) { // for (name in prop.Virtual) { // if (prop.Virtual.hasOwnProperty(name)) { // if (typeof prop.Virtual[name] !== "function") { // throw new Error("Virtual attribute is not allowed!"); // } // else { // // //虚方法前面加"Virtual_"前缀,在子类中要检查虚方法 // A.prototype[name] = prop.Virtual[name]; // temp[name] = prop.Virtual[name]; //用于检查是否包含父类的抽象方法/属性 或 接口方法/属性 // } // } // } // } //抽象类可以没有抽象成员 // if (!prop.Abstract) { // throw new Error("AbstractClass must have abstract methods!"); // } //放到外面的抽象成员,默认为公有抽象成员 for (name in prop.Abstract) { if (prop.Abstract.hasOwnProperty(name)) { // console.log(); //抽象方法前面加"Abstract_"前缀 A.prototype["Abstract_" + name] = prop.Abstract[name]; temp[name] = prop.Abstract[name]; //用于检查是否包含父类的抽象方法/属性 或 接口方法/属性 } } // //检查抽象类的公有方法+虚方法+抽象方法是否包含父类的抽象方法/属性 或 接口方法/属性 //检查抽象类的公有方法+虚方法+抽象方法是否包含父类的接口方法/属性 check(null, interface, temp); //静态属性/方法赋值 for (k in Static) { A[k] = Static[k]; } ////备份原型 //A.prototype.backUp_prototype = extendDeep(A.prototype); return A; } // //是否调用父类函数 // var base_flag = false; //创建普通类 //父类_parent可以为{Class: xx, Interface: xx},或者直接为xx类 function MyClass(_parent, _prop) { // console.log("length = " + arguments.length); var Static = null; // Private = null, // Public = null, // Origin = null; var k = null, name = null; var parentClass = null, interface = null, prop = null, temp = {}; // var temp = null; // //原型备份容器,用于创建实例时,恢复最初的原型(每次创建实例时,原型都保持不变)。 // var backUp_prototype = {}; //原型恢复标志,用于防止第一次创建实例时恢复原型 var mark_resume = false; //取出父类、接口 if (arguments.length === 1) { prop = arguments[0]; parentClass = null; interface = null; } //{Class: xx, Interface: xx} else if (typeof _parent === "object") { // if (parent.Class) if (!_parent.Class && !_parent.Interface) { throw new Error("Please add Class or Interface!"); } parentClass = _parent.Class; interface = _parent.Interface; prop = _prop; } //直接为xx类 else if (typeof _parent === "function") { parentClass = _parent; interface = null; // parent = _parent; // interface = null; prop = _prop; } else { throw new Error("arguments is not allowed!"); } //取出静态属性/方法、私有属性/方法、公有属性/方法 // Private = prop.Private; // Public = prop.Public; Static = prop.Static ? prop.Static : null; // //保存原始的私有属性,用于创建实例时,重置私有属性 // // var Origin = {}; // // Origin = Private // Origin = operate.extendDeep(Private); // YYC.Tool.extend.extend(Origin, Private); // //访问公共属性/方法的入口, // MyClass.Public = Public; // 本次调用所创建的类(构造函数) function F() { // console.log(mark_resume); //防止第一次创建实例时恢复原型 if (mark_resume) { //var t = F.prototype.backUp_prototype; //var m = F.prototype; //还原原型 //extendDeep(F.prototype.backUp_prototype, F.prototype); extendDeep(F.backUp_prototype, F.prototype); //F.prototype.backUp_prototype = extendDeep(F.prototype); } else { mark_resume = true; } // 如果当前处于实例化类的阶段,则调用Init原型函数 if (!initializing) { // console.log("F"); // // 如果父类存在,则实例对象的baseClass指向父类的原型 // // 这就提供了在实例对象中调用父类方法的途径 // if (parentClass) { // this.baseClass = parentClass.prototype; // // console.log(this.baseClass); // } this.Init && this.Init.apply(this, arguments); } // this.Public = Public; // console.log("F"); // if (this.) // console.log(this._m); // delete this._m; // delete F.prototype._m; // delete F.prototype._a; // this._m = null; // this._a = null; // delete F.prototype._a; /*不能删除私有成员和保护成员!否则类的成员就不能调用到私有和保护的成员了(因为已经删除了)! 对象的创建算法参考http://www.cnblogs.com/TomXu/archive/2012/02/06/2330609.html //删除私有成员和保护成员,这样外界就不能访问私有和保护成员了! for (name in this) { if (name.search(/(^_)|(^P_)/) !== -1) { delete F.prototype[name]; // this[name] = null; } } */ // console.log(count); // count++; //不使用MyClass.self!因为该属性为静态属性,如果创建了该类后,又创建了类A,则MyClass.self会指向类A! // MyClass的静态属性self指向创建的类的实例,可以通过self来访问实例的属性和方法 // MyClass.self = this; // Private.C(); // for (name in Private) { // Private[name].call(this); // } } // Private.C = Private.C.call(null, Public); // Private.call(F, null); // Private.M = (function (pub) { // return function () { // Private.M.call(null, arguments); // } // }(Public)); // for (name in Private) { // Private[name] = function () { // // console.log("1111111"); // return function () { // // console.log("222222222"); // return Private[name].call(this, arguments); // } // }; // } // Private.C = function () { // return function () { // Private.C.call(F, arguments); // } // }; // 如果此类需要从其它类扩展 if (parentClass) { initializing = true; //F.prototype = new parentClass(); F.prototype = extendDeep(parentClass.prototype); F.prototype.constructor = F; // for (name in parentClass.prototype) { // if (parentClass.prototype.hasOwnProperty(name)) { // //私有成员不继承 // if (!name.match(/^_/)) { // F.prototype[name] = parentClass.prototype[name]; // } // } // } // //删除父类的私有成员,保留本类的私有成员 // for (name in parentClass.prototype) { // if (parentClass.prototype.hasOwnProperty(name)) { // //私有成员以“_”开头,可能有多个“_”(多层继承) // if (!name.match(/^_+/)) { // // delete parentClass.prototype[name]; // F.prototype[name] = parentClass.prototype[name]; // } // } // } // console.log(F.prototype.constructor); // 如果父类存在,则实例对象的baseClass指向父类的原型。 // 这就提供了在实例对象中调用父类方法的途径。 //baseClass的方法是指向parentClass的,不是指向F(子类)的! //F.prototype.baseClass = parentClass.prototype; F.prototype[getNoRepeatStrInPrototype(parentClass.prototype, "baseClass")] = parentClass.prototype; initializing = false; } if (prop.Init) { // 如果此类继承自父类parent并且父类原型中存在同名函数name if (parentClass && typeof prop.Init === "function" && typeof F.prototype.Init === "function") { F.prototype.Init = function (name) { return function () { this.base = function () { return parentClass.prototype[name].apply(parentClass.prototype, arguments); }; //指向子类,可以用于模版模式 this.baseToSubClass = parentClass.prototype[name]; //执行fn并返回执行的结果 //此处的arguments为F.prototype[name]方法传入的形参。 return prop[name].apply(this, arguments); }; }("Init"); } else { F.prototype.Init = prop.Init; } } // Private.call(this); // if (parentClass && parentClass.prototype["JudgeDoubleHit"]) { // console.log(parentClass.toString()); if (parentClass) { for (name in parentClass.prototype) { if (parentClass.prototype.hasOwnProperty(name)) { //如果不是抽象方法/保护方法/私有方法/接口成员,则加入到temp中。 //用于添加父类的密封方法(因为子类并没有加入父类的密封方法)。 if (!name.match(/^Abstract_/) || !name.match(/^P_/) || !name.match(/^_/) || !name.match(/^Interface_/)) { temp[name] = parentClass.prototype[name]; } } } } // } // if (this.baseClass.Protected) { // if (this.baseClass.Protected.Sealed) { // for (k in this.baseClass.Protected.Sealed) { // temp[k] = this.baseClass.Protected.Sealed[k]; // } // } // } if (prop.Private) { //私有属性/方法直接覆盖 for (name in prop.Private) { if (prop.Private.hasOwnProperty(name)) { F.prototype[name] = prop.Private[name]; } } } // if (!prop.Public) { // throw new Error("Class must have public methods!"); // } // else { // } //保护成员 if (prop.Protected) { for (name in prop.Protected) { if (prop.Protected.hasOwnProperty(name)) { //检查虚方法,虚方法放到Public或Protected中 if (name === "Virtual") { addVirtual(prop["Protected"][name], F, temp); continue; } //密封的方法(不允许子类重写) if (name === "Sealed") { addSealed(prop["Protected"][name], F, temp); continue; } F.prototype[name] = prop.Protected[name]; //如果父类有保护抽象成员,此处检查子类的保护成员是否实现了父类的保护抽象成员 temp[name] = prop.Protected[name]; } } } // //虚方法(不能为虚属性) // if (prop.Virtual) { // for (name in prop.Virtual) { // if (prop.Virtual.hasOwnProperty(name)) { // if (typeof prop.Virtual[name] !== "function") { // throw new Error("Virtual attribute is not allowed!"); // } // else { // F.prototype[name] = prop.Virtual[name]; // temp[name] = prop.Virtual[name]; //加入temp // } // } // } // } if (prop.Abstract) { throw new Error("Only abstractClass can have abstract methods!"); } if (prop.Public) { // 覆盖父类的同名公有方法 for (name in prop.Public) { // console.log("for in name = " + name); // //私有属性/方法不加入到原型中 // if (name === "Private") { //// console.log("continue"); // continue; // } if (prop.Public.hasOwnProperty(name)) { //检查虚方法,虚方法放到Public或Protected中 if (name === "Virtual") { addVirtual(prop["Public"][name], F, temp); continue; } //密封的方法(不允许子类重写) if (name === "Sealed") { addSealed(prop["Public"][name], F, temp); continue; } // console.log("Public"); // console.log("name = " + name); // console.log("prop.Public[name] = " + prop.Public[name]); temp[name] = prop.Public[name]; //加入temp // 如果此类继承自父类parent并且父类原型中存在同名函数name if (parentClass && typeof prop.Public[name] === "function" && typeof F.prototype[name] === "function") { // console.log("parent!"); F.prototype[name] = function (name) { return function () { /*此处如果写成“this.base = parentClass.prototype[name];”,则在使用this.base()调用父类同名方法时, 父类同名方法的this指针是指向F的!(即指向子类,而不是指向父类!) 为什么???? 如: var Person = MyAbstract({ Init: function (name) { this.name = name; }, Public: { m: 1, getEmployeeID: function () { console.log(this.m); } } } }); var Employee = MyClass({ Init: function (name) { this.name = name; }, Public: { m: 100, getEmployeeID: function () { this.baseClass.getEmployeeID(); this.base(); } } }); var m = new Employee(); m.getEmployeeID(); //输出:1 100 分析: this.baseClass.getEmployeeID()的this指向Person,而this.base()的this指向Employee! 解决方案: 用apply修正this.base()中的this,使其指向父类。 */ // if (!this.base) { //此处不用创建闭包了!因为外面已经创建了闭包,name已经被保存了! this.base = function () { //这个写法也可以!为什么不用apply修正this也行??! //parentClass.prototype[name](); //此处的arguments为base方法传入的形参 //注意!要加上“return”,这样才能返回parentClass.prototype[name]的返回值 return parentClass.prototype[name].apply(parentClass.prototype, arguments); // this.baseClass. }; // } // if (!this.baseToSubClass) { //指向子类,可以用于模版模式 this.baseToSubClass = parentClass.prototype[name]; // } // this.base = function () { // // console.log(base_flag); // Private = { // }; //// base_flag = true; // return parent.prototype[name]; // }; // console.log("arg = " + arg); //执行fn并返回执行的结果 //此处的arguments为F.prototype[name]方法传入的形参。 return prop.Public[name].apply(this, arguments); }; }(name); } else { // console.log(); //公有属性 if (typeof (prop.Public[name]) !== "function") { F.prototype[name] = prop.Public[name]; } //公有方法 else { /* 如果不传入Public[name],而直接在自执行函数中调用Public[name],如 F.prototype[name] = function () { return function () { prop.Public[name].apply(this, arguments); }; } (); 或者写成: F.prototype[name] = function () { prop.Public[name].call(this, arguments); }; 这样的话,在创建实例时调用方法时,都会执行MyClass中的最后一个函数!见下例 var Person = MyClass({ Init: function (name) { this.name = name; }, getName: function () { console.log("getName"); }, getEmployeeID: function ($private) { console.log("Person getEmployeeID"); } }); var m = new Person("name"); m.getName(); //第一种和第二种写法此处会输出:"Person getEmployeeID" 这样执行的原因是: (引用自“深入理解JavaScript系列(16):闭包(Closures)”) 同一个父上下文中创建的闭包是共用一个[[Scope]]属性的。也就是说, 某个闭包对其中[[Scope]]的变量做修改会影响到其他闭包对其变量的读取。 这就是说:所有的内部函数都共享同一个父作用域。 也就是说,function里面的name都是共用父作用域中的name!所以此处F.prototype[name]被激活的时候, name都为最后一个值即"getEmployeeID"。 所以F原型上的方法都指向"getEmployeeID" 解决方案: 创建一个闭包来保存name的值。 */ F.prototype[name] = function (name) { return function () { return prop.Public[name].apply(this, arguments); //执行fn并返回执行的结果 }; }(name); } } } } } //检查公有成员和虚函数是否实现了抽象方法/属性 或 接口方法/属性 check(parentClass, interface, temp); //静态属性/方法赋值 for (k in Static) { F[k] = Static[k]; } //备份原型 //F.prototype.backUp_prototype = extendDeep(F.prototype); F.backUp_prototype = extendDeep(F.prototype); return F; }; YYC.Pattern.namespace("Frame").MyInterface = MyInterface; YYC.Pattern.namespace("Frame").MyAbstract = MyAbstract; YYC.Pattern.namespace("Frame").MyClass = MyClass; }());
我并不打算对代码详细说明,因为该文的重点在于展示重构的过程。因此我介绍下原始版本实现OOP的核心内容,具体请参考代码。
MyInterface为接口,MyAbstract为抽象类,MyClass为类。
创建一个接口,可以这样写:
var A = YYC.Frame.MyInterface("method1", "method2");
调用“YYC.Frame.MyInterface”时,会调用MyInterface函数,该函数会把参数解析,把方法和属性加上前缀“Interface_”,加入到内部函数I.prototype中,然后返回内部函数I。
因此,A就具有了接口方法和接口属性,但是我们不会直接使用接口(如创建A的实例,访问接口方法),因为接口的方法和属性(统称为成员)并没有实现,需要在继承接口的类中实现。
然后创建一个抽象类:
var B = YYC.Frame.MyAbstract({ Protected: { //保护成员 Abstract: { //保护抽象成员 }, Virtual: { //保护虚方法 }, P_proA: true, //保护属性 P_proM: function () { } //保护方法 }, Public: { //公有成员 Abstract: { //公有抽象成员 }, Virtual: { //公有虚方法 }, pubM: function () { }, //公有方法 pubA: 0 //公有属性 }, Private: { //私有成员 _priA: "", //私有属性 _priM: function () { } //私有方法 }, Abstract: { //公有抽象成员 }, Virtual: { //公有虚方法 } });
调用“YYC.Frame.MyAbstract”时,会调用MyAbstract函数,该函数会把参数解析,将公有成员、私有成员、保护成员都加入到内部函数A.prototype中(约定私有成员、保护成员的命名规则,约定使用框架者遵守访问权限)。
抽象成员(Abstract:{}中的成员)先加上前缀“Abstract_”,然后加入到A.prototype中(子类根据前缀来区分判断是否实现了父类的抽象成员)。
然后创建一个类,继承接口A和抽象类B:
var C = YYC.Frame.MyClass({Interface: A, Class: B},{ Init: function () { //构造函数 }, Protected: { //保护成员 }, Public: { Virtual: { }, method1: function () { }, method2: function () { } }, Private: { //私有成员 _priA: "", //私有属性 _priM: function () { } //私有方法 } });
调用“YYC.Frame.MyClass”时,会调用MyClass函数,该函数会把参数解析,将公有成员、私有成员、保护成员都加入到内部函数F.prototype中。
构造函数Init在F的构造函数function F(){}中调用,从而在创建C的实例时,会调用构造函数Init。
此处继承了接口A和抽象类B,因此会先用深拷贝的方法来将A、B的成员加入到F.prototype中,然后判断是否实现A的接口成员(根据“Interface_”前缀来判断)、是否实现B的抽象成员(根据“Abstract_”前缀来判断),如果没有实现会抛出异常。
//F.prototype = new parentClass(); F.prototype = extendDeep(parentClass.prototype);
此处继承使用深拷贝来实现,原因是为了解决下面的问题:
var Parent = YYC.Frame.MyClass({ Private:{ _a: [] }, Public: { add: function () { this._a.push("a"); } } }); var Sub1 = YYC.Frame.MyClass(Parent, {}); var Sub2 = YYC.Frame.MyClass(Parent, {}); var t = new Sub1(); t.add(); console.log(t.a); //["a"] var k = new Sub2(); console.log(k.a); //照理说应该为[],但实际上却是["a"]!
上例中的“t.add();”修改的是实例t的_a属性,实例t的_a属性与Parent.prototype._a指向同一个数组。因此修改实例t的_a就相当于修改了Parent.prototype._a。
修改类继承方式,改为通过深拷贝的方式拷贝父类原型的成员来实现继承:
F.prototype = extendDeep(parentClass.prototype);
这样实例t的_a属性和Parent.protype._a就指向不同的数组了。
为什么要重构
原始版本对其它的库有依赖(如依赖于namespace.js),有太多没用或错误的注释,混在一起的职责,过于庞大的函数,函数名、属性名不能很好地体现职责,多层嵌套的条件式等等。另外,最大的问题是没有对应的测试套件。
开始重构
回到本次重构中来,要进行重构,首先需要坚固的测试作保证。我使用Jasmine作为Javascript的测试工具。建议大家先可以看下javascript单元测试,里面介绍了单元测试的工具,包括Jasmine。
先编写几个主要的测试,测试是否解决了我之前发现的几个问题。
describe("oopFrame", function () { describe("测试Class", function () { describe("获得公有成员", function () { it("如果父类不存在,能够正确获得公有方法", function () { var Class = YYC.Frame.MyClass({ Public: { a: function () { this.b = 1; return 0; } } }); var cla = new Class(); var result = cla.a(); expect(result).toEqual(0); expect(cla.b).toEqual(1); }); }); }); describe("集成测试", function () { it("测试解决“若父类的属性为引用类型(数组或对象)a,则如果子类的实例s1对a进行修改或者sub调用修改a的方法,则第二次创建实例s2的a为修改过后的a!”的问题", function () { var Parent = YYC.Frame.MyAbstract({ Init: function () { console.log("Parent Init!"); }, Public: { a: [], } }); var Sub = YYC.Frame.MyClass(Parent, { Init: function () { }, Public: { } }); var t = new Sub(); t.a.push("a"); var m = new Sub(); expect(m.a).toEqual([]); }); it("测试解决“若父类Parent的属性为引用类型(数组或对象)a,有两个子类Sub1、Sub2。如果子类Sub1的实例s1对a进行修改或者sub调用修改a的方法,则子类Sub2的实例的a为修改过后的a!”的问题", function () { var Parent = YYC.Frame.MyAbstract({ Init: function () { console.log("Parent Init!"); }, Public: { a: [], add: function () { this.a.push("a"); } } }); var Sub1 = YYC.Frame.MyClass(Parent, { Init: function () { }, Public: { } }); var Sub2 = YYC.Frame.MyClass(Parent, { Init: function () { } }); var t = new Sub1(); t.a.push("a"); var k = new Sub2(); expect(k.a).toEqual([]); }); it("测试解决“若A1为抽象类,A2(抽象类)继承于A1,B(类)继承于A2,A1、A2、B都有同名方法a,A2和B在a方法中都通过this.baseClass调用父类同名方法。则如果B的实例b调用a方法,则A2、B的a方法中的this.baseClass均指向A2(照理说A2的this.baseClass应该指向A1)!”的问题", function () { var A1 = YYC.Frame.MyAbstract({ Public: { arr: [], a: function () { this.arr.push(1); } } }); var A2 = YYC.Frame.MyAbstract(A1, { Public: { a: function () { this.arr.push(2); this.baseClass.a.call(this, null); } } }); var B = YYC.Frame.MyClass(A2, { Public: { a: function () { this.arr.push(3); this._baseClass.a.call(this, null); return this.arr; } } }); var b = new B(); expect(b.a()).toEqual([3, 2, 1]); }); }); });
现在测试的覆盖面还不全,有些代码没有测试到。不过我们已经构建了主要的测试,剩下的测试可以在后续的重构中逐渐加入。
<%@ Page Language="C#" Inherits="System.Web.Mvc.ViewPage<dynamic>" %> <!DOCTYPE HTML> <html> <head id="Head1" runat="server"> <title>Jasmine Spec Runner</title> <link rel="stylesheet" type="text/css" href="<%=Url.Content("~/Scripts/jasmine/lib/jasmine-1.3.1/jasmine.css") %>" /> <script src="<%=Url.Content("~/Scripts/jasmine/lib/jasmine-1.3.1/jasmine.js") %>"></script> <script src="<%=Url.Content("~/Scripts/jasmine/lib/jasmine-1.3.1/jasmine-html.js") %>"></script> <!-- include source files here... --> <script src="<%=Url.Content("~/Scripts/jquery-1.7.js")%>"></script> <script src="../../Scripts/myTool/pattern/createObject/namespace.js"></script> <script src="<%=Url.Content("~/Scripts/myTool/frame/YOOP.js")%>"></script> <!-- include spec files here... --> <script src="<%=Url.Content("~/Scripts/jasmine/spec/helper/specHelper.js")%>"></script> <script src="../../Scripts/jasmine/spec/frameSpec/YOOPSpec.js"></script> </head> <body> <script type="text/javascript"> (function () { var jasmineEnv = jasmine.getEnv(); jasmineEnv.updateInterval = 1000; var htmlReporter = new jasmine.HtmlReporter(); jasmineEnv.addReporter(htmlReporter); jasmineEnv.specFilter = function (spec) { return htmlReporter.specFilter(spec); }; function execJasmine() { jasmineEnv.execute(); } var currentWindowOnload = window.onload; if (currentWindowOnload) { currentWindowOnload(); } execJasmine(); })(); </script> </body> </html>
在测试页面中运行测试,通过全部测试。
现在该框架通过了测试,能够正常工作。但是因为它采用命名空间模式,需要依赖于namespace.js库。
我想解除这个依赖,因此直接在框架中定义YYC命名空间,并且考虑到该框架的通用性较高,因此将其命名空间YYC.Frame更改为YYC。
修改框架为:
window.YYC = window.YYC || {}; ... YYC.MyInterface = MyInterface; YYC.MyAbstract = MyAbstract; YYC.MyClass = MyClass;
测试页面中去掉对namespace.js的引用。
类名MyInterface、MyAbstract、MyClass太长了,而且个人气息太浓,因此将类名改为Interface、AClass、Class。
原始版本:
A.prototype[name] = function (name) { return function () { //此处不用创建闭包了!因为外面已经创建了闭包,name已经被保存了! this.base = function () { //这个写法也可以!为什么不用apply修正this也行??! //parentClass.prototype[name](); //此处的arguments为base方法传入的形参 //注意!要加上“return”,这样才能返回parentClass.prototype[name]的返回值 return abstractClass.prototype[name].apply(abstractClass.prototype, arguments); }; //指向子类,可以用于模版模式 this.baseToSubClass = abstractClass.prototype[name]; //执行fn并返回执行的结果 //此处的arguments为F.prototype[name]方法传入的形参。 return prop.Public[name].apply(this, arguments); }; }(name);
原版中,子类使用this.base调用父类同名函数(函数中this指向父类同名函数),子类使用this.baseToSubClass调用父类同名函数(函数中this指向子类同名函数)。
考虑到一般都是使用this.baseToSubClass,因此将this.base改名为this.baseToParent,this.baseToSubClass改名为this.base,并不再使用this.baseToParent。
重构版本:
return function () { /* //此处不用创建闭包了!因为外面已经创建了闭包,name已经被保存了! this.baseToParrent = function () { //这个写法也可以!为什么不用apply修正this也行??! //parentClass.prototype[name](); //此处的arguments为baseToParrent方法传入的形参 //注意!要加上“return”,这样才能返回parentClass.prototype[name]的返回值 return parentClass.prototype[name].apply(parentClass.prototype, arguments); }; */ //指向子类,可以用于模版模式 this.base = parentClass.prototype[name]; //执行fn并返回执行的结果 //此处的arguments为F.prototype[name]方法传入的形参。 return prop[name].apply(this, arguments); };
分析代码结构,发现AClass与Class联系的比较紧密,而Interface则可以单独为一块,因此对代码进行分块:
(function(){ //A结构 (function(){ //A1结构 function Interface(){ }; YYC.Interface = Interface; }()); (function(){ //A2结构 function AClass(){ }; function Class(){ }; YYC.AClass = AClass; YYC.Class = Class; }()); }());
在工具函数的名字中加入前缀“_”,表示为私有函数。
将Interface、AClass、Class共用的工具函数提出来,放到A中。然后将AClass、Class共用的工具函数提出来放到A2中:
(function(){ //A结构 window.YYC = window.YYC || {}; /************************************************** String对象扩展 *********************************************************** 扩展方法: contain containIgnoreCase trim */ if (!String.prototype.contain) { String.prototype.contain = function (str) { var reg = new RegExp(str); if (this.match(reg)) { //用this指针指代本体 return true; } else { return false; } } } /*****************************************************************************************************************************/ ////获得函数的参数数组 //function argumentNames(fn) { // var names = fn.toString().match(/^[\s\(]*function[^(]*\(([^\)]*)\)/)[1].replace(/\s+/g, '').split(','); // return names.length == 1 && !names[0] ? [] : names; //}; //获得函数名 function _getFunctionName(fn) { var name = ""; if (!fn) { return null; } name = fn.toString().match(/^.*function\s*([^\(]*)/); return name === null ? name : name[1]; }; //判断是否为数组 function _isArray(val) { return Object.prototype.toString.call(val) === "[object Array]"; }; (function(){ //A1结构 function Interface(){ }; }()); (function(){ //A2结构 /* 深拷贝 */ function _extendDeep(parent, child) { var i = null, len = 0, toStr = Object.prototype.toString, sArr = "[object Array]", sOb = "[object Object]", type = "", _child = null; //数组的话,不获得Array原型上的成员。 if (toStr.call(parent) === sArr) { _child = child || []; for (i = 0, len = parent.length; i < len; i++) { type = toStr.call(parent[i]); if (type === sArr || type === sOb) { //如果为数组或object对象 _child[i] = type === sArr ? [] : {}; _extendDeep(parent[i], _child[i]); } else { _child[i] = parent[i]; } } } //对象的话,要获得原型链上的成员。因为考虑以下情景: //类A继承于类B,现在想要拷贝类A的实例a的成员(包括从类B继承来的成员),那么就需要获得原型链上的成员。 else if (toStr.call(parent) === sOb) { _child = child || {}; for (i in parent) { //if (parent.hasOwnProperty && parent.hasOwnProperty(i)) { // if (typeof parent[i] === 'object') { //null === 'object'也为true! type = toStr.call(parent[i]); if (type === sArr || type === sOb) { //如果为数组或object对象 _child[i] = type === sArr ? [] : {}; _extendDeep(parent[i], _child[i]); } else { _child[i] = parent[i]; } } //} } else { _child = parent; } return _child; }; //获得在原型prototype中不存在同名的str。 //如果有同名,则加上前缀"_" function _getNoRepeatStrInPrototype(prototype, str) { var new_str = ""; if (!prototype[str]) { return str; } new_str = "_" + str; return _getNoRepeatStrInPrototype(prototype, new_str); } function AClass(){ }; function Class(){ }; YYC.AClass = AClass; YYC.Class = Class; }()); }());
注意到_check函数太大,因此需要根据职责来提取出小函数。
_check有两个职责:
重构前:
function _check(parentClass, interface, children) { var name = ""; if (parentClass) { //检查是否实现了抽象方法/属性 for (name in parentClass.prototype) { if (parentClass.prototype.hasOwnProperty(name)) { if (name === "constructor") { continue; } if (name.contain("Abstract_")) { //抽象方法 if (typeof parentClass.prototype[name] === "function") { if (children[name.slice(9)] === undefined || typeof children[name.slice(9)] !== "function") { throw new Error("Abstract method '" + name + "' must be overwrited!"); } } //抽象属性 else { if (children[name.slice(9)] === undefined || typeof children[name.slice(9)] === "function") { throw new Error("Abstract attribute '" + name + "' must be overwrited!"); } } } } } } if (!interface) { return; } //检查是否实现了接口方法/属性 for (name in interface.prototype) { if (name === "constructor") { continue; } //接口方法 if (typeof interface.prototype[name] === "function") { if (children[name.slice(10)] === undefined || typeof children[name.slice(10)] !== "function") { throw new Error("Interface method '" + name + "' must be overwrited!"); } } //接口属性 else { if (children[name.slice(10)] === undefined || typeof children[name.slice(10)] === "function") { throw new Error("Interface attribute '" + name + "' must be overwrited!"); } } } };
重构后:
//检查子类的公有方法+虚方法+抽象方法是否包含父类的抽象方法/属性 或 接口方法/属性。 //不用hasOwnProperty判断!否则就检查不到是否包含了父类的抽象方法/属性 或 接口方法/属性。 function _check(parentClass, interface, children) { if (parentClass) { _checkAbstract(parentClass, children); } else if (interface) { _checkInterface(interface, children); } }; function _checkAbstract(parentClass, children) { var name = ""; for (name in parentClass.prototype) { if (parentClass.prototype.hasOwnProperty(name)) { if (name === "constructor") { continue; } if (name.contain("Abstract_")) { //抽象方法 if (typeof parentClass.prototype[name] === "function") { if (children[name.slice(9)] === undefined || typeof children[name.slice(9)] !== "function") { throw new Error("Abstract method '" + name + "' must be overwrited!"); } } //抽象属性 else { if (children[name.slice(9)] === undefined || typeof children[name.slice(9)] === "function") { throw new Error("Abstract attribute '" + name + "' must be overwrited!"); } } } } } }; function _checkInterface(interface, children) { var name = ""; for (name in interface.prototype) { if (name === "constructor") { continue; } //接口方法 if (typeof interface.prototype[name] === "function") { if (children[name.slice(10)] === undefined || typeof children[name.slice(10)] !== "function") { throw new Error("Interface method '" + name + "' must be overwrited!"); } } //接口属性 else { if (children[name.slice(10)] === undefined || typeof children[name.slice(10)] === "function") { throw new Error("Interface attribute '" + name + "' must be overwrited!"); } } } };
_checkAbstract、_checkInterface中的“if (children[name.slice(9)] === undefined || typeof children[name.slice(9)] !== "function") {”等条件语句很难理解,因此将其封装成函数:
function _checkAbstract(parentClass, children) { var name = ""; for (name in parentClass.prototype) { if (parentClass.prototype.hasOwnProperty(name)) { if (name === "constructor") { continue; } if (name.contain("Abstract_")) { //抽象方法 if (typeof parentClass.prototype[name] === "function") { if (_noMethodForAbstract(children, name) && _noMethodForAbstract(parentClass.prototype, name)) { throw new Error("Abstract method '" + name + "' must be overwrited!"); } } //抽象属性 else { if (_noAttritubeForAbstract(children, name) && _noAttritubeForAbstract(parentClass.prototype, name)) { throw new Error("Abstract attribute '" + name + "' must be overwrited!"); } } } } } }; function _checkInterface(interface, children) { var name = ""; for (name in interface.prototype) { if (name === "constructor") { continue; } //接口方法 if (typeof interface.prototype[name] === "function") { if (_noMethodForInterface(children, name) && _noMethodForInterface(parentClass.prototype, name)) { throw new Error("Interface method '" + name + "' must be overwrited!"); } } //接口属性 else { if (_noAttritubeForInterface(children, name) && _noAttritubeForInterface(parentClass.prototype, name)) { throw new Error("Interface attribute '" + name + "' must be overwrited!"); } } } }; function _noMethodForAbstract(_class, name) { return _class[name.slice(9)] === undefined || typeof _class[name.slice(9)] !== "function"; }; function _noAttritubeForAbstract(_class, name) { return _class[name.slice(9)] === undefined || typeof _class[name.slice(9)] === "function"; }; function _noMethodForInterface(_class, name) { return _class[name.slice(10)] === undefined || typeof _class[name.slice(10)] !== "function"; }; function _noAttritubeForInterface(_class, name) { return _class[name.slice(10)] === undefined || typeof _class[name.slice(10)] === "function"; };
现在让我们看下function Interface(){}中的代码:
//创建接口 //接口可以继承接口 function Interface(_parent, _method, _attribute) { var i = 0, args = null; var parent = null, method = null, attribute = null; if (typeof _parent === "function") { if (_getFunctionName(_parent) !== "I") { throw new Error("Interface must inherit interface!"); } else { parent = _parent; //形如“MyInterface(Parent, "A", "B", "GetName");” if (_method && !_isArray(_method)) { method = Array.prototype.slice.call(arguments, 1); attribute = null; } //形如“MyInterface(Parent, ["A", "B", "GetName"], ["a", "c"]);” else { method = _method; attribute = _attribute; } } // console.log(parent.toString()); } else { parent = null; //形如“MyInterface("A", "B", "GetName");” if (_method && !_isArray(_method)) { method = arguments attribute = null; } //形如“MyInterface(["A", "B", "GetName"], ["a", "c"]);” else { method = arguments[0]; attribute = arguments[1]; } } function I() { } // 如果此接口需要从其它接口扩展 if (parent) { I.prototype = new parent(); I.prototype.constructor = I; } // console.log("method = " + method); // console.log("attribute = " + attribute); // //形如“MyInterface(["A", "B", "GetName"], ["a", "c"]);” // if (isArray(method)) { //方法 for (i = 0; i < method.length; i++) { //加上前缀“Interface_” I.prototype["Interface_" + method[i]] = function () { throw new Error("This method must be overwrited!"); }; } //属性 if (attribute) { if (!isArray(attribute)) { throw new Error("Attribute must be array!"); } else { for (i = 0; i < attribute.length; i++) { //加上前缀“Interface_” I.prototype["Interface_" + attribute[i]] = 0; } } } // } // //形如“MyInterface("A", "B", "GetName");” // else { // args = Array.prototype.slice.call(arguments, 1); // //方法 // for (i = 0; i < args.length; i++) { // I.prototype[args[i]] = function () { // throw new Error("This method must be overwrited!"); // }; // } // } return I; };
该函数包含了太多的职责,应该把每一个职责提取为一个内部函数,然后再在Interface中调用这些内部函数:
//创建接口 //接口可以继承接口 function Interface(_parent, _method, _attribute) { var i = 0, args = null; var parent = null, method = null, attribute = null; function _getByParent() { if (typeof _parent === "function") { if (_getFunctionName(_parent) !== "I") { throw new Error("Interface must inherit interface!"); } else { parent = _parent; //形如“Interface(Parent, "A", "B", "GetName");” if (_method && !_isArray(_method)) { method = Array.prototype.slice.call(arguments, 1); attribute = null; } //形如“Interface(Parent, ["A", "B", "GetName"], ["a", "c"]);” else { method = _method; attribute = _attribute; } } } else { parent = null; //形如“Interface("A", "B", "GetName");” if (_method && !_isArray(_method)) { method = arguments attribute = null; } //形如“Interface(["A", "B", "GetName"], ["a", "c"]);” else { method = arguments[0]; attribute = arguments[1]; } } }; function _inherit() { I.prototype = new parent(); I.prototype.constructor = I; }; function _addMethod() { for (i = 0; i < method.length; i++) { //加上前缀“Interface_” I.prototype["Interface_" + method[i]] = function () { throw new Error("This method must be overwrited!"); }; } }; function _addAttribute() { if (attribute) { if (!_isArray(attribute)) { throw new Error("Attribute must be array!"); } else { for (i = 0; i < attribute.length; i++) { //加上前缀“Interface_” I.prototype["Interface_" + attribute[i]] = 0; } } } }; function I() { } _getByParent(); // 如果此接口需要从其它接口扩展 if (parent) { _inherit(); } //方法 _addMethod(); //属性 _addAttribute(); return I; };
AClass、Class中有一些重复的代码,将这些重复的代码提取为函数,放到结构A2中,供AClass、Class调用:
(function(){ //A2结构 //检查抽象成员 function _addAbstract(abstract, currentClass, temp) { var name = ""; for (name in abstract) { if (abstract.hasOwnProperty(name)) { //抽象方法前面加"Abstract_"前缀 currentClass.prototype["Abstract_" + name] = abstract[name]; temp[name] = abstract[name]; //加入temp } } }; //检查虚方法(不能为虚属性) function _addVirtual(virtual, currentClass, temp) { var name = ""; for (name in virtual) { if (virtual.hasOwnProperty(name)) { if (typeof virtual[name] !== "function") { throw new Error("Virtual attribute is not allowed!"); } else { currentClass.prototype[name] = virtual[name]; temp[name] = virtual[name]; //加入temp } } } }; //加入密封方法。 //没有实现检查子类是否重写了父类的密封方法,只是定义了一个规范。 function _addSealed(sealed, currentClass, temp) { var name = ""; for (name in sealed) { if (sealed.hasOwnProperty(name)) { currentClass.prototype[name] = sealed[name]; temp[name] = sealed[name]; //加入temp } } }; function _addStatic(_class, prop) { var Static = null; var k = null; Static = prop.Static ? prop.Static : null; //静态属性/方法赋值 for (k in Static) { _class[k] = Static[k]; } }; function _inherit(_class, parentClass) { _class.prototype = _extendDeep(parentClass.prototype); _class.prototype.constructor = _class; // 如果父类存在,则实例对象的baseClass指向父类的原型。 // 这就提供了在实例对象中调用父类方法的途径。 //baseClass的方法是指向parentClass的,不是指向F(子类)的! _class.prototype[_getNoRepeatStrInPrototype(parentClass.prototype, "baseClass")] = parentClass.prototype; }; function _addInit(_class, parentClass, prop) { if (prop.Init) { // 如果此类继承自父类parent并且父类原型中存在同名函数name if (parentClass && typeof prop.Init === "function" && typeof _class.prototype.Init === "function") { //if (parentClass) { _class.prototype.Init = function (name) { return function () { /* //此处不用创建闭包了!因为外面已经创建了闭包,name已经被保存了! this.baseToParrent = function () { //这个写法也可以!为什么不用apply修正this也行??! //parentClass.prototype[name](); //此处的arguments为baseToParrent方法传入的形参 //注意!要加上“return”,这样才能返回parentClass.prototype[name]的返回值 return parentClass.prototype[name].apply(parentClass.prototype, arguments); }; */ //指向子类,可以用于模版模式 this.base = parentClass.prototype[name]; //执行fn并返回执行的结果 //此处的arguments为F.prototype[name]方法传入的形参。 return prop[name].apply(this, arguments); }; }("Init"); } else { _class.prototype.Init = prop.Init; } } }; function _addPrivate(_class, private) { if (private) { //私有属性/方法直接覆盖 for (name in private) { if (private.hasOwnProperty(name)) { _class.prototype[name] = private[name]; } } } }; //检查抽象类的公有方法+虚方法+抽象方法是否包含父类的抽象方法/属性 或 接口方法/属性。 //不用hasOwnProperty判断!否则就检查不到是否包含了父类的抽象方法/属性 或 接口方法/属性。 function _check(parentClass, interface, children) { if (parentClass) { _checkAbstract(parentClass, children); } else if (interface) { _checkInterface(interface, children); } }; function _checkAbstract(parentClass, children) { var name = ""; for (name in parentClass.prototype) { if (parentClass.prototype.hasOwnProperty(name)) { if (name === "constructor") { continue; } if (name.contain("Abstract_")) { //抽象方法 if (typeof parentClass.prototype[name] === "function") { if (_noMethodForAbstract(children, name) && _noMethodForAbstract(parentClass.prototype, name)) { throw new Error("Abstract method '" + name + "' must be overwrited!"); } } //抽象属性 else { if (_noAttritubeForAbstract(children, name) && _noAttritubeForAbstract(parentClass.prototype, name)) { throw new Error("Abstract attribute '" + name + "' must be overwrited!"); } } } } } }; function _checkInterface(interface, children) { var name = ""; for (name in interface.prototype) { if (name === "constructor") { continue; } //接口方法 if (typeof interface.prototype[name] === "function") { if (_noMethodForInterface(children, name) && _noMethodForInterface(parentClass.prototype, name)) { throw new Error("Interface method '" + name + "' must be overwrited!"); } } //接口属性 else { if (_noAttritubeForInterface(children, name) && _noAttritubeForInterface(parentClass.prototype, name)) { throw new Error("Interface attribute '" + name + "' must be overwrited!"); } } } }; function _noMethodForAbstract(_class, name) { return _class[name.slice(9)] === undefined || typeof _class[name.slice(9)] !== "function"; }; function _noAttritubeForAbstract(_class, name) { return _class[name.slice(9)] === undefined || typeof _class[name.slice(9)] === "function"; }; function _noMethodForInterface(_class, name) { return _class[name.slice(10)] === undefined || typeof _class[name.slice(10)] !== "function"; }; function _noAttritubeForInterface(_class, name) { return _class[name.slice(10)] === undefined || typeof _class[name.slice(10)] === "function"; }; function AClass(){ }; function Class(){ }; YYC.AClass = AClass; YYC.Class = Class; }());
将AClass中的每一个职责提取为一个内部函数,然后再在AClass中调用这些内部函数:
//创建抽象类 //抽象类能够继承接口、抽象类以及实体类,但此处约定抽象类只能继承接口和抽象类,不能继承实体类! //(这样方便判断抽象类是否包含全部的父类(接口/抽象类)成员) function AClass(_parent, _prop) { var name = null, temp = {}; var parentClass = null, interface = null, prop = null; ////原型恢复标志,用于防止第一次创建实例时恢复原型 //var mark_resume = false; function _getByParent() { //if (arguments.length === 1) { if (_prop === undefined) { prop = _parent; parentClass = null; interface = null; } else if (typeof _parent === "object") { if (!_parent.Class && !_parent.Interface) { throw new Error("Please add AbstractClass or Interface!"); } if (_getFunctionName(_parent.Class) === "F" || _getFunctionName(_parent.Interface) === "F") { throw new Error("AbstractClass here can't inherit parentClass which is created by Class function!"); } parentClass = _parent.Class; interface = _parent.Interface; prop = _prop; } //_parent直接为xx,就表示父类为抽象类 else if (typeof _parent === "function") { if (_getFunctionName(_parent) === "F") { throw new Error("AbstractClass here can't inherit parentClass which is created by Class function!"); } parentClass = _parent; interface = null; prop = _prop; } else { throw new Error("arguments is not allowed!"); } }; function _prepareAndAddPublic() { if (prop.Public) { for (name in prop.Public) { if (prop.Public.hasOwnProperty(name)) { if (_prepareCheck("Public") === "continue") { continue; } _addPublic(); } } } }; function _addPublic() { if (parentClass && typeof prop.Public[name] === "function" && typeof A.prototype[name] === "function") { A.prototype[name] = function (name) { return function () { /* //此处不用创建闭包了!因为外面已经创建了闭包,name已经被保存了! this.baseToParrent = function () { //这个写法也可以!为什么不用apply修正this也行??! //parentClass.prototype[name](); //此处的arguments为baseToParrent方法传入的形参 //注意!要加上“return”,这样才能返回parentClass.prototype[name]的返回值 return parentClass.prototype[name].apply(parentClass.prototype, arguments); }; */ //指向子类,可以用于模版模式 this.base = parentClass.prototype[name]; //执行fn并返回执行的结果 //此处的arguments为F.prototype[name]方法传入的形参。 return prop.Public[name].apply(this, arguments); }; }(name); } else { A.prototype[name] = prop.Public[name]; } } function _prepareAndAddProtected() { if (prop.Protected) { for (name in prop.Protected) { if (prop.Protected.hasOwnProperty(name)) { if (_prepareCheck("Protected") === "continue") { continue; } A.prototype[name] = prop.Protected[name]; } } } }; function _prepareCheck(where) { //检查抽象成员,抽象成员放到Public或Protected中 if (name === "Abstract") { _addAbstract(prop[where][name], A, temp); return "continue"; } //检查虚方法,虚方法放到Public或Protected中 if (name === "Virtual") { _addVirtual(prop[where][name], A, temp); return "continue"; } //密封的方法(不允许子类重写) if (name === "Sealed") { _addSealed(prop[where][name], A, temp); return "continue"; } temp[name] = prop[where][name]; //用于检查是否包含父类的抽象方法/属性 或 接口方法/属性 return null; }; // 本次调用所创建的类(构造函数) function A() { } //取出父类、接口 _getByParent(); // 如果此接口需要从其它接口扩展 if (parentClass) { _inherit(A, parentClass); } //加入构造函数 //抽象类本身因为不能实例化,所以不调用构造函数。 //抽象类中的构造函数供子类构造函数中调用。 _addInit(A, parentClass, prop); _addPrivate(A, prop.Private); _prepareAndAddPublic(); //保护成员 _prepareAndAddProtected(); //放到外面的抽象成员,默认为公有抽象成员 _addAbstract(prop.Abstract, A, temp); _addStatic(A, prop); //检查抽象类的公有方法+虚方法+抽象方法是否包含父类的接口方法/属性 _check(null, interface, temp); return A; };
同理Class重构为:
//创建普通类 //父类_parent可以为{Class: xx, Interface: xx},或者直接为xx类 function Class(_parent, _prop) { //当前是否处于创建类的阶段。 var initializing = false; var name = null; var parentClass = null, interface = null, prop = null, temp = {}; //原型恢复标志,用于防止第一次创建实例时恢复原型 var mark_resume = false; function _getByParent() { if (_prop === undefined) { prop = _parent; parentClass = null; interface = null; } //{Class: xx, Interface: xx} else if (typeof _parent === "object") { if (!_parent.Class && !_parent.Interface) { throw new Error("Please add Class or Interface!"); } parentClass = _parent.Class; interface = _parent.Interface; prop = _prop; } //直接为xx类 else if (typeof _parent === "function") { parentClass = _parent; interface = null; prop = _prop; } else { throw new Error("arguments is not allowed!"); } }; function _addParentSealed() { for (name in parentClass.prototype) { if (parentClass.prototype.hasOwnProperty(name)) { //如果不是抽象方法/保护方法/私有方法/接口成员,则加入到temp中。 //用于添加父类的密封方法(因为子类并没有加入父类的密封方法)。 if (!name.match(/^Abstract_/) || !name.match(/^P_/) || !name.match(/^_/) || !name.match(/^Interface_/)) { temp[name] = parentClass.prototype[name]; } } } }; function _prepareAndAddPublic() { if (prop.Public) { for (name in prop.Public) { if (prop.Public.hasOwnProperty(name)) { if (_prepareCheck("Public") === "continue") { continue; } _addPublic(); } } } }; function _addPublic() { // 如果此类继承自父类parent并且父类原型中存在同名函数name if (parentClass && typeof prop.Public[name] === "function" && typeof F.prototype[name] === "function") { F.prototype[name] = function (name) { return function () { //指向子类,可以用于模版模式 this.base = parentClass.prototype[name]; //执行fn并返回执行的结果 //此处的arguments为F.prototype[name]方法传入的形参。 return prop.Public[name].apply(this, arguments); }; }(name); } else { F.prototype[name] = prop.Public[name]; } } function _prepareAndAddProtected() { if (prop.Protected) { for (name in prop.Protected) { if (prop.Protected.hasOwnProperty(name)) { if (_prepareCheck("Protected") === "continue") { continue; } F.prototype[name] = prop.Protected[name]; } } } }; function _prepareCheck(where) { //检查虚方法,虚方法放到Public或Protected中 if (name === "Virtual") { _addVirtual(prop[where][name], A, temp); return "continue"; } //密封的方法(不允许子类重写) if (name === "Sealed") { _addSealed(prop[where][name], A, temp); return "continue"; } temp[name] = prop[where][name]; //用于检查是否包含父类的抽象方法/属性 或 接口方法/属性 return null; }; _getByParent(); // 本次调用所创建的类(构造函数) function F() { //防止第一次创建实例时恢复原型 if (mark_resume) { //还原原型 _extendDeep(F.backUp_prototype, F.prototype); } else { mark_resume = true; } // 如果当前处于实例化类的阶段,则调用Init原型函数 if (!initializing) { this.Init && this.Init.apply(this, arguments); } /*不能删除私有成员和保护成员!否则类的成员就不能调用到私有和保护的成员了(因为已经删除了)! 对象的创建算法参考http://www.cnblogs.com/TomXu/archive/2012/02/06/2330609.html //删除私有成员和保护成员,这样外界就不能访问私有和保护成员了! for (name in this) { if (name.search(/(^_)|(^P_)/) !== -1) { delete F.prototype[name]; // this[name] = null; } } */ } // 如果此类需要从其它类扩展 if (parentClass) { initializing = true; _inherit(F, parentClass); initializing = false; } _addInit(F, parentClass, prop); if (parentClass) { _addParentSealed(); } _addPrivate(F, prop.Private); //保护成员 _prepareAndAddProtected(); if (prop.Abstract) { throw new Error("Only abstractClass can have abstract methods!"); } _prepareAndAddPublic(); //检查公有成员和虚函数是否实现了抽象方法/属性 或 接口方法/属性 _check(parentClass, interface, temp); _addStatic(F, prop); //备份原型 F.backUp_prototype = _extendDeep(F.prototype); return F; };
AClass和Class中的局部属性temp的职责是存储该类成员的名称,从而用于检查该类成员是否实现了接口或者父类的抽象成员。
因此,将temp改名为children,这样能反映职责。