前面的理论先不管了,直接解析代码。
计算器程序的UML类图
代码解析
在讲解模式之前,先介绍两个辅助函数/对象
View Code
/** * 用于创建接口的类,并带有检查功能 */ var Interface = function(name, methods) { //检查参数个数 if(arguments.length != 2) { //错误显示不能准确定位 throw new Error('创建接口的对象需要2个参数:接口名,其方法名组成的数组'); } //确保第二个参数是数组类型 if(!(methods instanceof Array)) { throw new Error('创建接口的第二参数应为数组类型'); } //为接口创建两个属性,并赋值 this.name = name; this.methods = []; //存储接口方法 for(var i=0; methods[i]; i++) { if(typeof methods[i] !== 'string') { throw new Error('接口的方法名应为字符串'); } this.methods.push(methods[i]); } }; /** * 检查创建的实例是否存在接口中的所有方法 * 使用方法:把此函数紧跟创建实例语句后面 * @param {Object} object 实例 * @param {multi} interfaces 接口列表 */ Interface.ensureImplement = function(object, interfaces) { //检查参数个数 if(arguments.length < 2) throw new Error('接口类的静态ensureImplement方法参数不能少于2个'); //子类实现的接口 var intface = null; //检查子类是否实现了接口所有方法 for(var i=1; arguments[i]; i++) { intface = arguments[i]; //判断传递的参数是否都是合法的接口实例 if(intface.constructor !== Interface) throw new Error('用ensureImplement检查时,发现'+intface+'不是合法的接口'); //获取接口的所有方法 var methods = intface.methods; //检查子类是否实现了接口中的所有方法 for(var j=0; methods[j]; j++) { var method = methods[j]; if(!object[method] || typeof object[method]!='function') { throw new Error('没有实现'+intface.name+'接口的方法:'+method); } } } };
创建接口的示例:
//创建运算类接口 var Operation = new Interface('Operation', ['getResult']); //----------------------------------------------------- //... IFactory.prototype.getResult = function(a, b) { var oper = this.factoryMethod(); //判断实例是否实现了Operation接口中的所有方法 Interface.ensureImplement(oper, Operation); return oper.getResult(a, b); };
下面是辅助函数extend()用于继承操作
View Code
/** * 实现原型对象继承 */ var extend = function(subClass, superClass) { var F = function() {}; F.prototype = superClass.prototype; subClass.prototype = new F(); subClass.prototype.constructor = subClass; subClass.superclass = superClass.prototype; if(superClass.prototype.constructor === Object.prototype.constructor) { superClass.prototype.constructor = superClass; } };
工厂方法模式实现计算器程序中所有的类统计:
/* * 工厂方法 * IFactory * AddFactory * SubFactory * MulFactory * DivFactory * * Operation * OperationAdd * OperationSub * OperationMul * OperationDiv */
创建运算类接口,并用各个运算符实现具体类。其中用到了接口。
//创建运算类接口 var Operation = new Interface('Operation', ['getResult']); //具体的实现接口的加法运算类 var OperationAdd = function() {}; //implements Operation OperationAdd.prototype.getResult = function(a, b) { return a+b; }; //减法运算类 var OperationSub = function() {}; //implements Operation OperationSub.prototype.getResult = function(a, b) { return a-b; }; //...
继承可以有类式继承和原型式继承,本程序实现了这两种方法
类式继承实现工厂方法的继承
View Code
/* * 抽象类 * 创建器,声明工厂方法 */ var IFactory = function() {}; /** * 1. 获取具体运算类实例,并检查是否实现了接口 * 2. 用获取的实例调用同名运算方法 * @param {Number} a * @param {Number} b * @return {Number} */ IFactory.prototype.getResult = function(a, b) { var oper = this.factoryMethod(); //判断实例是否实现了Operation接口中的所有方法 Interface.ensureImplement(oper, Operation); return oper.getResult(a, b); }; IFactory.prototype.factoryMethod = function() { throw new Error('IFactory是一个抽象类,子类需要实现factoryMethod方法'); }; /* * 加法工厂类 * 具体的创建器,实现IFactory中的工厂方法,并返回具体的运算符对象 */ var AddFactory = function() {}; //继承IFactory抽象类 extend(AddFactory, IFactory); AddFactory.prototype.factoryMethod = function() { return new OperationAdd; //子类创建运算类实例 };
原型式继承实现搭建工厂方法
//原型式继承使用的辅助函数 var clone = function(object) { //检查object是不是字面量对象 if(Object.prototype.toString.call(object) !== '[object Object]') { throw new Error('在clone函数中传递的不是字面量对象'); } var F = function() {}; F.prototype = object; return new F(); }; //模仿抽象类 var iFactory = { getResult: function(a, b) { //获取运算类对象 var oper = this.factoryMethod(); //通过具体运算类,返回结果 return oper.getResult(a, b); }, factoryMethod: function() { throw new Error('子类需要实现factoryMethod方法'); } }; //原型式继承iFactory对象 var subFactory = clone(iFactory); //覆盖iFactory中的方法 subFactory.factoryMethod = function() { return new OperationSub(); };
现在已经把整个程序业务写好了,看看怎样使用我们刚才所创建的一大推东西
//--------------------- test -- (function() { try{ //创建加法工厂实例 var addFactory = new AddFactory(); //用加法工厂创建出来的加法类实现两参数相加 var result = addFactory.getResult(4, 3); console.log(result); //原型式继承的测试 //console.log(subFactory.getResult(3, 1)); } catch(e) { console.log(e.message); } })();
客户端使用是不是很简单呢。
项目开发中遇到的问题:
在辅助函数/对象中大量使用了throw new Error()语句,当错误发生时会允许你自定义错误提示信息。但这个提示却不能定位的那么完美。