Skip to content

Instantly share code, notes, and snippets.

@hoodwink73
Last active September 21, 2022 18:36
Show Gist options
  • Save hoodwink73/8204691 to your computer and use it in GitHub Desktop.
Save hoodwink73/8204691 to your computer and use it in GitHub Desktop.
Make a collection an iterator which can iterate over a model property
// 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;
};
// 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
*/
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.
// 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