Patrones de Diseño
http://vimeo.com/44094122
https://github.com/shichuan/javascript-patterns/tree/master/design-patterns
Patrón Singleton
[creación de objetos]
http://www.addyosmani.com/resources/essentialjsdesignpatterns/book/#singletonpatternjavascript
http://www.pixelovers.com/patrones-diseno-javascript-patron-singleton-1698384
https://github.com/shichuan/javascript-patterns/blob/master/design-patterns/singleton.html
La idea general de este patrón es la de asegurar que una clase genera una única instancia, es decir, limitar la instanciación de una clase a un único objeto
var mySingleton = (function () {
// Instance stores a reference to the Singleton
var instance;
function init() {
// Singleton
// Private methods and variables
function privateMethod(){
console.log( "I am private" );
}
var privateVariable = "Im also private";
return {
// Public methods and variables
publicMethod: function () {
console.log( "The public can see me!" );
},
publicProperty: "I am also public"
};
};
return {
// Get the Singleton instance if one exists
// or create one if it doesn't
getInstance: function () {
if ( !instance ) {
instance = init();
}
return instance;
}
};
})();
// Usage:
>>> var singleA = {}, singleB = {};
>>> singleA === singleB
false
>>> singleA = mySingleton.getInstance()
>>> singleB = mySingleton.getInstance()
>>> singleA === singleB
true
Patrón Factory
[creación de objetos]
http://www.addyosmani.com/resources/essentialjsdesignpatterns/book/#factorypatternjavascript
https://github.com/shichuan/javascript-patterns/blob/master/design-patterns/factory.html
- El objetivo de este patrón es el de crear objetos
- En vez de crear objetos directamente con new, utilizaremos un objeto Factory al que le pediremos que tipo de objeto queremos y éste objeto lo instanciará y nos lo devolverá
- Este patrón es util en las siguientes situaciones:
- Cuando necesitamos repetir operaciones al crear objetos similares
- Cuando necesitamos generar diferentes instancias de objetos dependiendo del entorno
- Cuando trabajamos con pequeños objetos o componentes que comparten las mismas propiedades
// Types.js - Constructors used behind the scenes
// A constructor for defining new cars
function Car( options ) {
// some defaults
this.doors = options.doors || 4;
this.state = options.state || "brand new";
this.color = options.color || "silver";
}
// A constructor for defining new trucks
function Truck( options){
this.state = options.state || "used";
this.wheelSize = options.wheelSize || "large";
this.color = options.color || "blue";
}
// FactoryExample.js
// Define a skeleton vehicle factory
function VehicleFactory() {}
// Define the prototypes and utilities for this factory
// Our default vehicleClass is Car
VehicleFactory.prototype.vehicleClass = Car;
// Our Factory method for creating new Vehicle instances
VehicleFactory.prototype.createVehicle = function ( options ) {
if( options.vehicleType === "car" ){
this.vehicleClass = Car;
}else{
this.vehicleClass = Truck;
}
return new this.vehicleClass( options );
};
// Create an instance of our factory that makes cars
var carFactory = new VehicleFactory();
var car = carFactory.createVehicle( {
vehicleType: "car",
color: "yellow",
doors: 6 } );
// Test to confirm our car was created using the vehicleClass/prototype Car
// Outputs: true
console.log( car instanceof Car );
// Outputs: Car object of color "yellow", doors: 6 in a "brand new" state
console.log( car );
Approach #1: Modify a VehicleFactory instance to use the Truck class
var movingTruck = carFactory.createVehicle( {
vehicleType: "truck",
state: "like new",
color: "red",
wheelSize: "small" } );
// Test to confirm our truck was created with the vehicleClass/prototype Truck
// Outputs: true
console.log( movingTruck instanceof Truck );
// Outputs: Truck object of color "red", a "like new" state
// and a "small" wheelSize
console.log( movingTruck );
Approach #2: Subclass VehicleFactory to create a factory class that builds Trucks
function TruckFactory () {}
TruckFactory.prototype = new VehicleFactory();
TruckFactory.prototype.vehicleClass = Truck;
var truckFactory = new TruckFactory();
var myBigTruck = truckFactory.createVehicle( {
state: "omg..so bad.",
color: "pink",
wheelSize: "so big" } );
// Confirms that myBigTruck was created with the prototype Truck
// Outputs: true
console.log( myBigTruck instanceof Truck );
// Outputs: Truck object with the color "pink", wheelSize "so big"
// and state "omg. so bad"
console.log( myBigTruck );
Patrón Iterator
https://github.com/shichuan/javascript-patterns/blob/master/design-patterns/iterator.html
- El objetivo de este patrón es el de encapsular la lógica para recorrer los datos de un objeto
- En el patron Iterator nuestro objeto necesita proporcionar un metodo next() que nos devuelva el siguiente elemento de la secuencia
- Tambien se suele proporcionar en este objeto el método hasNext() para verificar si hemos llegado al final de la secuencia de datos
var agg = (function () {
var index = 0,
data = [1, 2, 3, 4, 5],
length = data.length;
return {
next: function () {
var element;
if (!this.hasNext()) {
return null;
}
element = data[index];
index = index + 2;
return element
},
hasNext: function () {
return index < length;
}
};
}());
// this loop logs 1, then 3, then 5
while (agg.hasNext()) {
console.log(agg.next());
}
Patrón Mixins
[estructura, sub‐classing, code re‐use]
http://www.addyosmani.com/resources/essentialjsdesignpatterns/book/#mixinpatternjavascript
- Los Mixins son una manera de recolectar funcionalidades a traves de las extensiones
- Los Mixins los podemos considerar objetos cuyos atributos y metodos pueden ser compartidos por otros objetos prototipos.
var myMixins = {
moveUp: function(){
console.log( "move up" );
},
moveDown: function(){
console.log( "move down" );
},
stop: function(){
console.log( "stop! in the name of love!" );
}
};
// A skeleton carAnimator constructor
function carAnimator(){
this.moveLeft = function(){
console.log( "move left" );
};
}
// A skeleton personAnimator constructor
function personAnimator(){
this.moveRandomly = function(){ /*..*/ };
}
// Extend both constructors with our Mixin
_.extend( carAnimator.prototype, myMixins );
_.extend( personAnimator.prototype, myMixins );
// Create a new instance of carAnimator
var myAnimator = new carAnimator();
myAnimator.moveLeft();
myAnimator.moveDown();
myAnimator.stop();
// Outputs:
// move left
// move down
// stop! in the name of love!
Patrón Decorator
[estructura, sub-classing, code re-use]
http://www.addyosmani.com/resources/essentialjsdesignpatterns/book/#decoratorpatternjavascript
- El patron Decorator se centra en cómo extender las funcionalidades de los objetos
- Con el patrón Decorator podemos añadir/sobreescribir dinamicamente comportamiento a los métodos existentes de un objeto
Ejemplo: Decorating Constructors With New Functionality
// A vehicle constructor
function vehicle( vehicleType ){
// some sane defaults
this.vehicleType = vehicleType || "car";
this.model = "default";
this.license = "00000-000";
}
// Test instance for a basic vehicle
var testInstance = new vehicle( "car" );
console.log( testInstance );
// Outputs:
// vehicle: car, model:default, license: 00000-000
// Lets create a new instance of vehicle, to be decorated
var truck = new vehicle( "truck" );
// New functionality we're decorating vehicle with
truck.setModel = function( modelName ){
this.model = modelName;
};
truck.setColor = function( color ){
this.color = color;
};
// Test the value setters and value assignment works correctly
truck.setModel( "CAT" );
truck.setColor( "blue" );
console.log( truck );
// Outputs:
// vehicle:truck, model:CAT, color: blue
// Demonstrate "vehicle" is still unaltered
var secondInstance = new vehicle( "car" );
console.log( secondInstance );
// Outputs:
// vehicle: car, model:default, license: 00000-000
Example 2: Decorating Objects With Multiple Decorators
// The constructor to decorate
function MacBook() {
this.cost = function () { return 997; };
this.screenSize = function () { return 11.6; };
}
// Decorator 1
function Memory( macbook ) {
var v = macbook.cost();
macbook.cost = function() {
return v + 75;
};
}
// Decorator 2
function Engraving( macbook ){
var v = macbook.cost();
macbook.cost = function(){
return v + 200;
};
}
// Decorator 3
function Insurance( macbook ){
var v = macbook.cost();
macbook.cost = function(){
return v + 250;
};
}
var mb = new MacBook();
Memory( mb );
Engraving( mb );
Insurance( mb );
// Outputs: 1522
console.log( mb.cost() );
// Outputs: 11.6
console.log( mb.screenSize() );
Patrón Façade
- El objetivo del patron Façade es simplificar la interfaz (los metodos/propiedades "públicos") ocultando el código complejo. Tambien se utiliza para desacoplar nuestro código de las API's de librerias externas
var addMyEvent = function( el,ev,fn ){
if( el.addEventListener ){
el.addEventListener( ev,fn, false );
}else if(el.attachEvent){
el.attachEvent( "on" + ev, fn );
} else{
el["on" + ev] = fn;
}
};
var module = (function() {
var _private = {
i:5,
get : function() {
console.log( "current value:" + this.i);
},
set : function( val ) {
this.i = val;
},
run : function() {
console.log( "running" );
},
jump: function(){
console.log( "jumping" );
}
};
return {
facade : function( args ) {
_private.set(args.val);
_private.get();
if ( args.run ) {
_private.run();
}
}
};
}());
// Outputs: "running", 10
module.facade( {run: true, val:10} );
Patrón Observer – Subscriber/Publisher
http://www.addyosmani.com/resources/essentialjsdesignpatterns/book/#observerpatternjavascript
http://www.jspatterns.com/book/7/observer.html
- El principal objetivo de este patrón es el de desacoplar las partes de un código.
En vez de tener un objeto llamando al método de otro objeto, con este patron un objeto se subscribe a la actividad de otro objeto y recibe una notificación cuando esta actividad se produce.
Subscriber = Observer Publisher = Subject
var publisher = {
subscribers: {
any: [] // event type: subscribers
},
subscribe: function (fn, type) {
type = type || 'any';
if (typeof this.subscribers[type] === "undefined") {
this.subscribers[type] = [];
}
this.subscribers[type].push(fn);
},
unsubscribe: function (fn, type) {
this.visitSubscribers('unsubscribe', fn, type);
},
publish: function (publication, type) {
this.visitSubscribers('publish', publication, type);
},
visitSubscribers: function (action, arg, type) {
var pubtype = type || 'any',
subscribers = this.subscribers[pubtype],
i,
max = subscribers.length;
for (i = 0; i < max; i += 1) {
if (action === 'publish') {
subscribers[i](arg);
} else {
if (subscribers[i] === arg) {
subscribers.splice(i, 1);
}
}
}
}
};
var s1 = {log: console.log},
s2 = {err: console.error},
s3 = {warn: console.warn};
publisher.subscribe(s1.log);
publisher.subscribe(s2.err);
publisher.subscribe(s3.warn);
publisher.publish({hello: "World"});
publisher.unsubscribe(s2.err);
publisher.publish("hello");
publisher.subscribe(s1.log, "log");
publisher.publish({obj: "log this object"}, "log");
function makePublisher(o) {
var i;
for (i in publisher) {
if (publisher.hasOwnProperty(i) && typeof publisher[i] === "function") {
o[i] = publisher[i];
}
}
o.subscribers = {any: []};
}
var paper = {
daily: function () {
this.publish("big news today");
},
monthly: function () {
this.publish("interesting analysis", "monthly");
}
};
makePublisher(paper);
var joe = {
drinkCoffee: function (paper) {
console.log('Just read ' + paper);
},
sundayPreNap: function (monthly) {
console.log('About to fall asleep reading this ' + monthly);
}
};
paper.subscribe(joe.drinkCoffee);
paper.subscribe(joe.sundayPreNap, 'monthly');
paper.daily();
paper.daily();
paper.daily();
paper.monthly();
makePublisher(joe);
joe.tweet = function (msg) {
this.publish(msg);
};
paper.readTweets = function (tweet) {
alert('Call big meeting! Someone ' + tweet);
};
joe.subscribe(paper.readTweets);
joe.tweet("hated the paper today");
Patrón Mediator
http://www.addyosmani.com/resources/essentialjsdesignpatterns/book/#mediatorpatternjavascript
https://github.com/shichuan/javascript-patterns/blob/master/design-patterns/mediator.html
- El patrón Mediator es un patrón de comportamiento que nos permite utilizar una unica interfaz a traves de la cual se pueden comunciar las diferentes partes de un sistema (ej. torre control aeropuerto)
- En Javascript este patron se suele implementar como un objeto compartido a traves del cual los otros objetos (modulos) de nuestro sistema se pueden comunicar (patron Observer centralizado)
var mediator = (function(){
// Storage for topics that can be broadcast or listened to
var topics = {};
// Subscribe to a topic, supply a callback to be executed
// when that topic is broadcast to
var subscribe = function( topic, fn ){
if ( !topics[topic] ){
topics[topic] = [];
}
topics[topic].push( { context: this, callback: fn } );
return this;
};
// Publish/broadcast an event to the rest of the application
var publish = function( topic ){
var args;
if ( !topics[topic] ){
return false;
}
args = Array.prototype.slice.call( arguments, 1 );
for ( var i = 0, l = topics[topic].length; i < l; i++ ) {
var subscription = topics[topic][i];
subscription.callback.apply( subscription.context, args );
}
return this;
};
return {
Publish: publish,
Subscribe: subscribe,
installTo: function( obj ){
obj.subscribe = subscribe;
obj.publish = publish;
}
};
}());