• [设计模式]创建接口


    写在前面:

          在有许多程序员参与的大型项目中,接口起着至关重要的作用。程序员常常需要使用还未编写的出来的API。或者需要提供一些占位代码以免延误开发进度。接口在这种场合中的重要性表现在许多方面。它们记载着API,可作为程序员正式交流的工具。在占位代码被替换为最终的API时,你立刻就能知道所需方法是否得到了实现。在开发过程中,如果API发生了变化,只要新的API实现了同样的接口,它就能完美替换原有API。

    目录:

    • 用注释描述接口
    • 用属性检查模仿接口
    • 用鸭式辨型模仿接口
    • 通用的接口实现方法

    在JS中使用接口

    用注释描述接口

    /*
     * interface Composite {
     *     function add(child);
     *     function remove(child);
     *     function getChild(child);
     * }
     * 
     * interface FormItem {
     *     function save();
     * }
     * 
     */
    var CompositeForm = function() {    //implements Composite and FormItem
        //...
    };
    
    CompositeForm.prototype = {
        //Implement the interface Composite
        add: function(child) {
            //...
        },
        remove: function(child) {
            //...
        },
        getChild: function(child) {
            
        },
        //Implement this interface FormItem
        save: function() {
            
        }
    }

    用属性检查模仿接口

    所有类都明确地声明自己都实现了哪些接口。

    那些想与这些类打交道的对象可以针对这些声明进行检查

    /*
     * interface Composite {
     *     function add(child);
     *     function remove(child);
     *     function getChild(child);
     * }
     * 
     * interface FormItem {
     *     function save();
     * }
     * 
     */
    var CompositeForm = function(id, method) {    //implements Composite and FormItem
        //显式声明类实现哪些接口
    this.implementsInterfaces = ['Composite', 'FormItem'];
        //...
    };
    //...
    /*
     * 先判断传递的对象是否实现了需要的接口
     * 检查通过后,才能调用需要的方法
     * @param {CompositeForm} formInstance
     */
    function addForm(formInstance) {
        if(!implements(formInstance, 'Composite', 'FormItem')) {
            throw new Error("对象没有实现需要的接口");
        }
        //...
    }
    //检查对象是否实现了提供的接口
    //但并不能保证类实现了接口的所有方法
    function implements(object) {
        //第2个参数开始是需要的不确定数量的接口
        for(var i=1; i<arguments.length; i++) {
            var interfaceName = arguments[i];               //接口
            var found = false;       //标识是否实现了接口
            var interfaceNames = object.implementsInterfaces; 
            for(var j=0; j<interfaceNames.length; j++) {
                if(interfaceNames[j] == interfaceName) {
                        //在接口声明的数组中找到了
                    found = true;
                    break;
                }
            }
            if(!found) {
                return false;        //没有实现需要的接口
            }
        }
        return true;
    }

    这种方法优点:它对类所实现的接口提供了文档说明。如果需要的接口不在一个类宣称支持的接口之列时抛出一个错误。

    缺点:它并不能确保类真正实现了自称实现的接口。

    用鸭式辨型模仿接口

    它把对象实现的方法集作为判断它是不是某个类的实例的唯一标准。这种技术可以用来判断一个类是否实现了某个接口。背后的思想:如果对象有与接口定义的方法同名的所有方法,那么就可以认为它实现了这个接口。

    //interfaces
    var Composite = Interface('Composite', ['add', 'remove', 'getChild']);
    var FormItem = Interface('FormItem', ['save']);
    
    //CompositeForm class
    var CompositeForm = function(id, method) {
        //...
    };
    
    function addForm(formInstance) {
        //检查接口的方法formInstance对象是否都实现了
        ensureImplements(formInstance, 'Composite', 'FormItem');
        //...
    }

    类没有声明自己实现了哪些接口,这降低了代码的可重用性。

    通用接口实现方法

    var Composite = Interface('Composite', ['add', 'remove', 'getChild']);
    var FormItem = Interface('FormItem', ['save']);
    
    //CompositeForm class
    var CompositeForm = function(id, method) {      //implements Composite and FormItem
        //...
    };
    
    function addForm(formInstance) {
        //检查接口的方法formInstance对象是否都实现了
        Interface.ensureImplements(formInstance, 'Composite', 'FormItem');
        //...
    }

    上面使用接口(Interface)类实现

    /**
     * 定义接口的辅助函数
     * @param {String} name 接口名
     * @param {Array} methods 所有方法组成的数组
     */
    var Interface = function(name, methods) {
        if(arguments.length != 2) {
            throw new Error('需要传递2个参数:接口名,方法名数组。');
        }
        this.name = name;
        this.methods = [];
        for(var i=0, len=methods.length; i<len; i++) {
            if(typeof methods[i] != 'string' ) {
                throw new Error('方法名应该是字符串类型');
            }
            this.methods.push(methods[i]);
        }
    };
    
    Interface.ensureImplements = function(object) {
        //判断参数的个数不少于2个
        if(arguments.length<2) {
            throw new Error('至少需要2个参数');
        }
        //检查对象的方法包含支持的接口的所有方法
        for(var i=1; i<arguments.length; i++) {
            var interface = arguments[i];           //待验证的接口
            //判断接口的类型
            if(interface.constructor !== Interface) {
                throw new Error('不是特定的接口');
            }
            var methods = interfaceName.methods;        //接口定义的所有方法
            for(var j=0, len=methods.length; j<len; j++) {
                var method = methods[j];
                if(!object[method] || typeof object[method] != 'function') {
                    throw new Error('没有实现接口'+interface.name+'中的方法'+method);
                }
            }
        }
    }

    一个理想的软件系统应该为所有类定义接口。

        接口提供了一种用以说明一个对象应该具有哪些方法的手段。尽管它可以表明(暗示)这些方法的作用,但它并不规定这些方法应该如何实现。有了接口,可以按对象提供的特性对它们进行分组。

        JS编写接口使用中的最大问题在于,无法强迫其他程序员遵守你定义的接口。

    使用接口的难点在于判断是否必要使用它,因为它并不总是不可或缺的,还降低效率

  • 相关阅读:
    访问者模式
    解释器模式
    享元模式
    职责链模式
    中介者模式
    单例模式
    桥接模式
    命令模式
    迭代器模式
    Python 学习笔记15 类
  • 原文地址:https://www.cnblogs.com/mackxu/p/2941325.html
Copyright © 2020-2023  润新知