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 !!
Design patterns are reusable solutions to commonly occurring problems in software design. They are proven solutions, easily reusable and expressive. They lower the size of your codebase, prevent future refactoring, and make your code easier to understand by other developers.
Interfaces are capable of describing the wide range of shapes that JavaScript objects can take. In addition to describing an object with properties, interfaces are also capable of describing function types. To describe a function type with an interface, we give the interface a call signature.
The easiest way to see how interfaces work is to start with a simple example: function printLabel(labelledObj: { label: string }) { console. log(labelledObj. label); } let myObj = {size: 10, label: "Size 10 Object"}; printLabel(myObj);
Design patterns provide a standard terminology and are specific to particular scenario. For example, a singleton design pattern signifies use of single object so all developers familiar with single design pattern will make use of single object and they can tell each other that program is following a singleton pattern.
ES6 has added syntactical sugar to the language. Below is the ES6 implementation of the same example.
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()
Try playing around with the code by removing the methods in the Map
class.
Hope it explains better to the people coming from oops background
Completed the book example and here is the working 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);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With