Created
April 20, 2013 17:21
-
-
Save mavame/5426716 to your computer and use it in GitHub Desktop.
My interpretation of Chapter 2 from Pro JavaScript Design Patterns
http://www.amazon.com/Pro-JavaScript-Design-Patterns-Object-Oriented/dp/159059908X
This file contains 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
// Ch2 from Pro JavaScript Design Patterns | |
// Interfaces in JS!! | |
/* | |
interface Composite { | |
function add(child); | |
function remove(child); | |
function getChild(index); | |
} | |
interface FormItem { | |
function save(); | |
} | |
*/ | |
// In this example, CompositeForm declares that it implements two interfaces, | |
// Composite and FormItem. If it does not implement an interface, an error | |
// will be thrown. | |
var CompositeForm = function(id, method, action) { // implements Composite and FormItem | |
// Attribute checking | |
this.implementsInterfaces = ['Composite', 'FormItem']; | |
}; | |
// Implement Composite interface | |
CompositeForm.prototype.add = function(child) {}; | |
CompositeForm.prototype.remove = function(child) {}; | |
CompositeForm.prototype.getChild = function(index) {}; | |
// Implement the FormItem interface | |
CompositeForm.prototype.save = function(){}; | |
function addForm(formInstance) { | |
if(!implements(formInstance, 'Composite', 'FormItem')) { | |
throw new Error('Object does not implement reguired interface'); | |
} | |
} | |
function implements(object) { | |
for(var i = 1; i < arguments.length; i++) { // looping through all args after 1st one | |
var interfaceName = arguments[i]; | |
var interfaceFound = false; | |
for(var j = 0; j < object.implementsInterfaces.length; j++) { | |
if(object.implementsInterfaces[j] == interfaceName) { | |
interfaceFound = true; | |
break; | |
} | |
if(!interfaceFound) { | |
return false; // An interface was not found | |
} | |
} | |
} | |
return true; // all interfaces were found | |
} | |
// However, in this form it just checks whether a Class "says" it implements something | |
// but it does not ensure that the methods are there. | |
// Duck typing | |
// "If it walks like a duck and quacks like a duck, it is a duck". | |
var Composite = new Interface('Composite', ['add', 'remove', 'getChild']); | |
var FormItem = new Interface('FormItem', ['save']); | |
var CompositeForm = function(id, method, action) {}; | |
function addForm(formInstance) { | |
ensureImplements(formInstance, Composite, FormItem); | |
// This function will throw an error if a required method is not implemented | |
// should look similar to "implements" above | |
} | |
// But this required the helper method "ensureImplements" | |
// The Interface Class | |
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; | |
this.methods = []; | |
for(i = 0, len = methods.length; i < len; i++) { | |
if(typeof methods[i] !== 'string') { | |
throw new Error('Interface constructor expects methods to be passed in as 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 expects 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'); | |
} | |
} | |
} | |
} | |
// Example of Using the Interface Class | |
// In the end, we want to be able to run this code: | |
var ResultFormatter = function(resultsObject) { | |
this.resultsObject = resultsObject; | |
} | |
ResultsFormatter.prototype.renderResults = function() { | |
var dateOfTest = this.resultsObject.getDate(); | |
var resultsArray = this.resultsObject.getResults(); | |
var resultsContainer = document.createElement('div'); | |
var resultsHeader = document.createElement('h3'); | |
resultsHeader.innerHTML = 'Test Results from ' + dateOfTest.toUTCString(); | |
resultsContainer.appendChild(resultsHeader); | |
var resultsList = document.createElement('ul'); | |
resultsContainer.appendChild(resultsList); | |
for(i = 0, len = resultsArray.length; i < len; i++) { | |
var listItem = document.createElement('li'); | |
listItem.innerHTML = resultsArray[i]; | |
resultsList.appendChild(listItem); | |
} | |
return resultsContainer; | |
}; | |
// 1. Before Interface; ResultFormatter with loose check that is instanceOf TestResult | |
var ResultFormatter = function(resultsObject) { | |
if(!resultsObject instanceOf TestResult) { | |
throw new Error('ResultsFormatter: constructor requires an instance of TestResult as an argument'); | |
} | |
this.resultsObject = resultsObject; | |
} | |
// But does getDate and getResults really exist? | |
// 2. Replace instanceOf check with an Interface | |
// In this example, we will be sure getDate and getResults exists in resultObject | |
// by declaring an Interface to check against | |
var ResultSet = new Interface('ResultSet', ['getDate', 'getResult']); | |
var ResultsFormatter = function(resultsObject) { | |
Interface.ensureImplements(resultsObject, ResultSet); | |
this.resultsObject = resultsObject; | |
}; | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Hi,
Can you please explain me interface with attributes with a working example.
Thanks and regards,
Tanveer Singh