JavaScript中的接口模式的运行示例或工作演示

时间:2012-10-24 00:09:02

标签: javascript oop design-patterns interface

我正在阅读“专业javascript设计模式”一书,并且在理解第2章中给出的“界面”模式时遇到了一些困难,因为没有完整的代码示例演示了这种模式的使用。

我正在寻找一些帮助理解这个模式,一些正在运行的代码示例可能在jsfiddle等上。

这个模式在第14-22页的书中有解释,我不明白的主要观点是调用“addForm”方法的位置和方式。要么 如果有人可以用一些测试数据和对象完成ResultFormatter示例,这对理解模式非常有帮助。

“Pro Javascript Design Patterns”一书的代码可以从http://jsdesignpatterns.com/下载,这是第2章。

感谢您的帮助!!

3 个答案:

答案 0 :(得分:9)

我已经看到了以不同方式实现的模式,但这个想法很简单:

  1. 你有一些类 - 你的界面 - 只是指定一些函数的名称。 (您可能希望有一个名为Interface的类,您的实际接口实例化,因此您的接口类型为Interface)
  2. 然后你有一些其他类来实现这样的接口。这意味着第二个类必须至少具有接口指定的所有函数。
  3. 最后,你还有其他一些功能,它希望接收一个实现接口的对象。在你提到的示例代码中,这个函数是addForm,它需要一个实现'Composite'和'FormItem'接口的对象。
  4. 此函数然后循环遍历它所期望的接口的所有方法,并检查传递给它的对象是否也具有这些方法。如果在传递给函数的对象中找不到来自其中一个接口的方法,则它确定该对象未实现该接口,并抛出异常。
  5. 由于涉及的开销,有些人可能会发现这种模式不实用,但鉴于Javascript缺乏对接口的原生支持,这不是一个糟糕的解决方案。有些人可能还会发现在Javascript中使用小型项目的接口是过度的。

    示例

    var Interface = function(name, methods) {
        this.name = name;
        this.methods = [];
    
        if (methods.constructor == Array)
            this.methods = methods;
        else if (methods.constructor == String)
            this.methods[0] = methods;
        else
            throw new Error("Interface must define methods as a String or an Array of Strings");
    };
    
    var InterfaceHelper  = {
        ensureImplements : function(obj, interfaces) {
           // If interfaces is not an array, assume it's a function pointer
           var toImplement = interfaces.constructor == Array ? interfaces : [interfaces];
           var interface;
    
           // For every interface that obj must implement:
           for (var i = 0, len = toImplement.length; i < len; i++) {
              interface = toImplement[i];
    
              // Make sure it indeed is an interface
              if (interface.constructor != Interface)
                 throw new Error("Object trying to implement a non-interface. "
                 + interface.name + " is not an Interface.");
    
              // Make sure obj has all of the methods described in the interface
              for (var j = 0, interfaceLen = interface.methods.length; j < interfaceLen; j++)
                 if (!obj[interface.methods[j]])
                    throw new Error("Interface method not implemented. " 
                    + interface.name + " defines method " + interface.methods[j]);
           }
    
           return true;
        }
    };
    
    var Drawable = new Interface("Drawable", ["onDraw"]);
    
    var Surface = function() {
       this.implements = ["Drawable"];
    
       this.onDraw = function() {
          console.log("Surface Drawing");
       };
    };
    

    <强>用法

    var myDrawableSurface = new Surface();
    
    // Returns true
    InterfaceHelper.ensureImplements(myDrawableSurface, Drawable);
    
    // Returns false (Error thrown)
    InterfaceHelper.ensureImplements(myDrawableSurface, Array);
    

答案 1 :(得分:4)

完成了本书的示例,这是工作jsfiddle, -

var Interface = function(name, methods) {
    if (arguments.length != 2) {
        throw new Error("Interface constructor called with " + arguments.length + "arguments, but expected exactly 2.");
    }

    this.name = name;
    this.methods = [];

    for (var i = 0, len = methods.length; i < len; i++) {
        if (typeof methods[i] !== 'string') {
            throw new Error("Interface constructor expects method names to be " + "passed in as a string.");
        }

        this.methods.push(methods[i]);
    }
};

// Static class method.
Interface.ensureImplements = function(object) {
    if (arguments.length < 2) {
        throw new Error("Function Interface.ensureImplements called with " + arguments.length + "arguments, but expected at least 2.");
    }

    for (var i = 1, len = arguments.length; i < len; i++) {
        var interface = arguments[i];

        if (interface.constructor !== Interface) {
            throw new Error("Function Interface.ensureImplements expects arguments" + "two and above to be instances of Interface.");
        }

        for (var j = 0, methodsLen = interface.methods.length; j < methodsLen; j++) {
            var method = interface.methods[j];

            if (!object[method] || typeof object[method] !== 'function') {
                throw new Error("Function Interface.ensureImplements: object " + "does not implement the " + interface.name + " interface. Method " + method + " was not found.");
            }
        }
    }
};

function Map() {}

Map.prototype.centerOnPoint = function(x,y) {
    alert('center=> x: ' + x + ', y: ' + y);
};

Map.prototype.zoom = function(x){
    alert('zoom : ' + x);
}

Map.prototype.draw = function(){
    alert('draw');
};

var map = new Map();
var DynamicMap = new Interface('DynamicMap', ['centerOnPoint', 'zoom', 'draw']);

function displayRoute(mapInstance) {
    Interface.ensureImplements(mapInstance, DynamicMap);
    mapInstance.centerOnPoint(12, 34);
    mapInstance.zoom(5);
    mapInstance.draw();
}

displayRoute(map);​

答案 2 :(得分:0)

ES6为语言添加了语法糖。下面是同一个例子的ES6实现。

&#13;
&#13;
class Interface {
    constructor(name, methods) {
        if (arguments.length < 2) {
            throw new Error('An Interface expects atleast 2 arguments ' + arguments.length
                + ' arguments passed')
            
        }
        this.name = name
        this.methods = []
        methods.forEach(method => {
            if (typeof method !== 'string') {
                throw new Error('Interface expects all the method names to be passed as as a string ' +
                    method + ' is a ' + typeof method)
            }
            this.methods.push(method)
        }, this);
    }

    static ensureImplements(object) {
        if(arguments.length < 2) {
            throw new Error("Function Interface.ensureImplements called with " +
                arguments.length + "arguments, but expected at least 2.")
        }

        for (let i = 1, len=arguments.length; i < len; i++) {
            const interf = arguments[i]
            if(interf.constructor !== Interface) {
                throw new Error('Function expects arguments two or above to be instaces of Interface' )
            }

            for(let j = 0, methodsLen = interf.methods.length; j < methodsLen; j++) {
                const method = interf.methods[j]
                if(!object[method] || !typeof object[method] === 'function') {
                    throw new Error('Does not implement the method the interface' + interf.name + 'Interface.Method '
                    + method + ' not found')
                }
            }
        }
    }
}

const DynamicMap = new Interface('DynamicMap', ['centerOnPoint', 'zoom', 'draw'])

class Map {
    constructor() {
        Interface.ensureImplements(this, DynamicMap)
    }
    centerOnPoint() {
        console.log('Moving to center')
    }
    zoom() {
        console.log('Zooming in')
    }

    draw() {
        console.log('Drawing map')
    }
}

const mapInstance = new Map()
&#13;
&#13;
&#13;

通过删除Map类中的方法,尝试使用代码。 希望它能更好地解释来自oops背景的人