Created
January 28, 2018 16:33
-
-
Save garata/cc3a94fed9a5da038646706b85d45238 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Simple implementation of interfaces in JavaScript | |
* | |
* @description | |
* | |
* Advantages of interfaces: | |
* + Self-documenting | |
* + Encapsulation | |
* + Code reuse | |
* | |
* The interface allows you to quickly understand what methods implements | |
* the class, and hide details of the implementation of these methods. | |
* | |
* If you have already develop object oriented software you may be acquainted | |
* with the interface concept and you know how to use the class that implements it, | |
* thus interface increases the chance of reusing existing classes and interfaces. | |
*/ | |
// From https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/keys | |
if (!Object.keys) { | |
Object.keys = (function () { | |
'use strict'; | |
var hasOwnProperty = Object.prototype.hasOwnProperty, | |
hasDontEnumBug = !({toString: null}).propertyIsEnumerable('toString'), | |
dontEnums = [ | |
'toString', | |
'toLocaleString', | |
'valueOf', | |
'hasOwnProperty', | |
'isPrototypeOf', | |
'propertyIsEnumerable', | |
'constructor' | |
], | |
dontEnumsLength = dontEnums.length; | |
return function (obj) { | |
if (typeof obj !== 'object' && (typeof obj !== 'function' || obj === null)) { | |
throw new TypeError('Object.keys called on non-object'); | |
} | |
var result = [], prop, i; | |
for (prop in obj) { | |
if (hasOwnProperty.call(obj, prop)) { | |
result.push(prop); | |
} | |
} | |
if (hasDontEnumBug) { | |
for (i = 0; i < dontEnumsLength; i++) { | |
if (hasOwnProperty.call(obj, dontEnums[i])) { | |
result.push(dontEnums[i]); | |
} | |
} | |
} | |
return result; | |
}; | |
}()); | |
} | |
/** | |
* @constructor Interface | |
* | |
* @param {String} interfaceName The interface name | |
* @param {Object} interfaceMembers The interface methods | |
* @return {Object} The interface object is instanceof Interface | |
*/ | |
function Interface(interfaceName, interfaceMembers) { | |
if (!(this instanceof Interface)) { | |
return new Interface(interfaceName, interfaceMembers); | |
} | |
var interfaceObj = this, keys = Object.keys(interfaceMembers); | |
for (var i = 0, li = keys.length; i < li; i++) { | |
var memberName = interfaces[i]; | |
interfaceObj[memberName] = function() { | |
Interface.interfaceError(interfaceName, memberName); | |
}; | |
} | |
interfaceObj.name = interfaceName; | |
return interfaceObj; | |
}; | |
/** | |
* @method Interface.interfaceError | |
* | |
* @param {String} interfaceName The interface name | |
* @param {String} interfaceMember The interface method name | |
*/ | |
Interface.interfaceError = function(interfaceName, interfaceMember) { | |
throw Error('InterfaceError: Class does not implement interface member ' + interfaceName + '.' + interfaceMember + '()'); | |
}; | |
/** | |
* @method Interface.implement | |
* | |
* @param {String} obj The object which should implement list of interfaces | |
* @param {String} [interfaces] The list of interfaces | |
* @return {Boolean} If object implement all interfaces then return true, else | |
* throw exception | |
*/ | |
Interface.implement = function(obj, interfaces) { | |
var interfaces = [].slice.call(arguments, 1); | |
for (var i = 0, li = interfaces.length; i < li; i++) { | |
var _interface = interfaces[i], keys = Object.keys(_interface); | |
for (var j = 0, lj = keys.length; j < lj; j++) { | |
var interfaceMember = keys[j], isFunction = typeof _interface[interfaceMember] === 'function'; | |
if (isFunction && !obj[interfaceMember]) { | |
Interface.interfaceError(_interface.name, interfaceMember); | |
} | |
} | |
} | |
return true; | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment