设计模式工厂方法模式<二>

前面的理论先不管了,直接解析代码。

计算器程序的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()语句,当错误发生时会允许你自定义错误提示信息。但这个提示却不能定位的那么完美。

原文地址:https://www.cnblogs.com/mackxu/p/factorymehod2.html