zoukankan      html  css  js  c++  java
  • 设计模式工厂方法模式<二>

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

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

  • 相关阅读:
    PHP实现无限极分类
    html2canvas生成并下载图片
    一次线上问题引发的过程回顾和思考,以更换两台服务器结束
    Intellij IDEA启动项目报Command line is too long. Shorten command line for XXXApplication or also for
    mq 消费消息 与发送消息传参问题
    idea 创建不了 java 文件
    Java switch 中如何使用枚举?
    Collections排序
    在idea 设置 git 的用户名
    mongodb添加字段和创建自增主键
  • 原文地址:https://www.cnblogs.com/mackxu/p/factorymehod2.html
Copyright © 2011-2022 走看看