Skip to content

Instantly share code, notes, and snippets.

@mavame
Created April 20, 2013 17:21
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mavame/5426716 to your computer and use it in GitHub Desktop.
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
// 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;
};
@tanveersingh926
Copy link

Hi,

Can you please explain me interface with attributes with a working example.

Thanks and regards,
Tanveer Singh

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment