10

I am reading the "pro javascript design patterns" book and finding little difficulty in understanding the "Interface" pattern given in the book chapter 2 as there isn't a complete code example demonstrating the use of this pattern.

I am looking for some help understanding this pattern with some running code example may be on jsfiddle etc.

This pattern is explained in the book pages 14 - 22, main point I am not understanding is where and how "addForm" method is called. OR if somebody can complete the ResultFormatter example with some test data and object this will really be very helpful in understanding the pattern.

Code for the book "Pro Javascript Design Patterns" can be downloaded from http://jsdesignpatterns.com/ and this is Chapter 2.

Thanks for the help !!

4

3 回答 3

9

我见过以不同方式实现的模式,但想法很简单:

  1. 你有一些类——你的接口——它只是指定了一些函数的名称。(您可能希望有一个名为 Interface 的类,您的实际接口实例化该类,这样您的接口就属于 Interface 类型)
  2. 然后你有一些其他类实现了这样的接口。这意味着第二个类必须至少具有接口指定的所有功能。
  3. 最后,您在其他地方还有一些其他功能,它期望接收一个实现接口的对象。在您提到的示例代码中,此函数是 addForm,它需要一个实现“Composite”和“FormItem”接口的对象。
  4. 然后,此函数循环遍历它期望的接口的所有方法,并检查您传递给它的对象是否也具有这些方法。如果在传递给函数的对象中找不到来自某个接口的方法,则确定该对象未实现该接口,并引发异常。

由于涉及的开销,有些人可能会发现这种模式不切实际,但鉴于 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);
于 2012-11-01T14:30:11.353 回答
5

完成了本书的例子,这里是工作的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);​
于 2012-11-01T19:11:16.110 回答
2

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

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()

尝试通过删除Map类中的方法来玩弄代码。希望它能更好地向来自 oop​​s 背景的人解释

于 2017-08-09T22:05:43.227 回答