Last active
September 21, 2022 18:36
-
-
Save hoodwink73/8204691 to your computer and use it in GitHub Desktop.
Make a collection an iterator which can iterate over a model property
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
// make a collection an iterator which can iterate over a model property | |
// Pattern completely inspired by Stoyan Stefanov's Iterator pattern(checkout the book JavaScript Patterns) | |
// Implementation | |
// collection = [ | |
// {"handle": "@fat", "status": "Studying philosophy"}, | |
// {"handle: "@addyosmani", "status": "Teaching how to web"} ]; | |
// var iterator = makeIterator(collection, "handle"); | |
// iterator.next() => "@fat" | |
// iterator.next() => "@addyosmani" | |
// iterator.next() => null | |
var makeIterator = function (collection, property) { | |
var agg = (function (collection) { | |
var index = 0; | |
var length = collection.length; | |
return { | |
next: function () { | |
var element; | |
if (!this.hasNext()) { | |
return null; | |
} | |
element = collection[index][property]; | |
index = index + 1; | |
return element; | |
}, | |
hasNext: function () { | |
return index < length; | |
}, | |
rewind: function () { | |
index = 0; | |
}, | |
current: function () { | |
return collection[index]; | |
} | |
}; | |
})(collection); | |
return agg; | |
}; |
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
// Decorators are a way to dynamically alter/augment the behavior of a | |
// object at runtime | |
// Creating Decorator Pattern using Lists | |
// It doesn't need inheritence | |
// the constructor | |
var Sale = function (price) { | |
this.price = price || 100; | |
this.decorator_list = []; | |
}; | |
Sale.decorators = {}; | |
// the decorators | |
Sale.decorators.fedtax = { | |
getPrice: function (price) { | |
return price + price * 5 / 100; | |
} | |
}; | |
Sale.decorators.quebec = { | |
getPrice: function (price) { | |
return price + price * 7.5 * 100; | |
} | |
}; | |
Sale.decorators.money = { | |
getPrice: function (price) { | |
return "$" + price.toFixed(2); | |
} | |
}; | |
// the decorate method | |
Sale.prototype.decorate = function (decorator) { | |
this.decorators_list.push(decorator); | |
}; | |
// the concerned method decorated by decorators | |
Sale.protype.getPrice = function () { | |
var i, name, newPrice; | |
var price = this.price; | |
var max = this.decorators_list.length; | |
for (i = 0; i < max; i += 1) { | |
name = this.decorators_list[i]; | |
newPrice = Sale.decorators[name].getPrice(price); | |
} | |
return newPrice; | |
}; | |
// This implementation makes the method to be decorated a lot more responsible | |
// what if you had a simple method and you wanted to make it decoratable | |
// a simple `getPrice` price like this | |
Sale.prototype.getPrice = function () { | |
return this.price; | |
}; | |
// we can create a method `makeMethodDecoratable` to create simple method like | |
// this decoratable | |
// but for that we have to the change the structure of the decorators_list | |
// the new structure will be like | |
this.decorators_list = { | |
'getPrice': [ | |
// push here the different implementation of getPrice from different decorators | |
] | |
}; | |
// and we have to change the `decorate` method for this | |
Sale.prototype.decorate = function ( decoratorName ) { | |
var decorator = Sale.decorators[decoratorName]; | |
var list = this.decorators_list; | |
if (!decorator) { | |
throw new Error( 'The decorator you mentioned does not exist for this object' ); | |
} | |
for ( decoratableMethod in decorator ) { | |
if ( !(decoratableMethod in list) ) { | |
list[decoratableMethod] = []; | |
} | |
list[decoratableMethod].push(decorator[decoratableMethod]); | |
} | |
}; | |
// now lets build the makeMethodDecoratable method | |
// it will take in a simple method and turn it into a decoratable method | |
Sale.prototype.makeMethodDecoratable = function (methodName) { | |
var method, decoratableMethod, decorators_list; | |
var self = this; | |
method = this[methodName]; | |
if ( !method ) { | |
throw Error(' The method name you passed does not exist on the object'); | |
} | |
decorators_list = this.decorators_list; | |
decoratableMethod = function () { | |
var decorators = decorators_list[method]; | |
var calculateNextValue = function (previousDecorator, currentDecorator) { | |
var previousValue; | |
previousValue = previousDecorator.call(self); | |
return currentDecorator.bind(self, previousValue); | |
}; | |
return decorators.reduce(calculateNextValue, method); | |
}; | |
return decoratableMethod; | |
}; | |
var sale = new Sale(100); | |
sale.decorate('fedtax'); | |
sale.decorate('quebec'); | |
sale.decorate('money'); | |
sale.getPrice() | |
/* | |
100 | |
*/ | |
sale.makeMethodDecoratable('getPrice'); | |
sale.getPrice(); | |
/* | |
$112.88 | |
*/ | |
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
JavaScript Design Patterns by Stoyan Stefanov is one of the most recommended JavaScript book you would come across the Internet. | |
I have already gone through it once. | |
But I always come back to it, to revise, and sometimes plot if I could apply any of the pattern to my work. | |
Here is a collection of patterns I use or intend to use. Sometimes, they are just as it is in the book or sometimes, I try augment it and make it better. |
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
// Strategy pattern lets you choose from a pool | |
// of algorithms dynamically | |
var validator = { | |
// all available checks | |
types: {}, | |
// error messages in the current | |
// validation session | |
messages: [], | |
// current validation config | |
// name: validation type | |
config: {}, | |
// the interface method | |
// `data` is key => value pairs | |
validate: function (data) { | |
var i, msg, type, checker, result_ok; | |
// reset all messages | |
this.messages = []; | |
for (i in data) { | |
if (data.hasOwnProperty(i)) { | |
type = this.config[i]; | |
checker = this.types[type]; | |
} | |
if (!type) { | |
// no need to validate | |
continue; | |
} | |
if (!checker) { | |
throw { | |
name: "ValidationError", | |
message: "No handler to validation type " + type | |
}; | |
} | |
result_ok = checker.validate(data[i]); | |
if (!result_ok) { | |
msg = "Invalid value for *" + i + "*, " + checker.instructions; | |
this.messages.push(msg); | |
} | |
} | |
return this.hasErrors(); | |
}, | |
hasErrors: function () { | |
return !this.messages.length; | |
} | |
}; | |
// various algorithms for the client to | |
// choose from | |
validator.types.isNonEmpty = { | |
validate: function ( value ) { | |
return value !== ''; | |
}, | |
instructions: 'the value cannot be empty' | |
}; | |
validator.types.isNumber = { | |
validate: function ( value ) { | |
return !isNaN( value ); | |
}, | |
instructions: 'the value can only be a valid number' | |
}; | |
validator.types.isAlphaNum = { | |
validate: function ( value ) { | |
return !/[^a-z0-9]/i.test(value); | |
}, | |
instructions: "the value can only contain characters and numbers, no special symbols" | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment