Design patterns in javascript

30
Design patterns in Javascript Miao Siyu

description

 

Transcript of Design patterns in javascript

Page 1: Design patterns in javascript

Design patterns in Javascript

Miao Siyu

Page 2: Design patterns in javascript

Why need design patterns

• Easy & nature: read, use/follow, extend/re-use, co-operator(expose api, split work…)…

• Efficiency & memory cost/gc…

Page 3: Design patterns in javascript

books• 'Patterns Of Enterprise Application

Architecture' Martin Fowler• 'JavaScript Patterns' Stoyan Stefanov• 'Pro JavaScript Design Patterns' Ross Harmes

and Dustin Diaz• 'Learning JavaScript Design Patterns' Addy

Osmani• …

Page 4: Design patterns in javascript

Categories Of Design Pattern

• Creational Design Patterns Constructor, Factory, Abstract, Prototype, Singleton and Builder

• Structural Design Patterns Decorator, Facade, Flyweight, Adapter and Proxy

• Behavioral Design Patterns Iterator, Mediator, Observer and Visitor

Page 5: Design patterns in javascript

Create an object• var newObject = {};• var newObject = Object.create(…);• var newObject = new Class();

http://dmitrysoshnikov.com/ecmascript/javascript-the-core/

Page 6: Design patterns in javascript

Define properties• newObject.someKey = …;• newObject[“someKey”] = …;• Object.defineProperty(newObject, “someKey”,{ // property attributes here: value, writable… })• function Car(model, color){ this.model = model; this.color = color } Car.prototype.run = function(){console.log(“run”)};

Page 7: Design patterns in javascript

Prototype pattern

• var anotherCar = Object.create( someCar );• var beget = (function () { function F() {}; return function (proto) { F.prototype = proto; return new F(); }; })();

Clone using prototype: avoid the inherent cost

Page 8: Design patterns in javascript

Singleton patternvar Singleton = (function () {

var instantiated;function init() {

return {publicMethod: function () {},publicProperty: 'test'

};}return {

getInstance: function () {if (!instantiated) {

instantiated = init();}return instantiated;

}};

})();Singleton.getInstance().publicMethod();

Anonymous Function

Page 9: Design patterns in javascript

Module patternvar myNamespace = (function () {

var myPrivateVar = 0;var myPrivateMethod = function (someText) {

console.log(someText);};return {

myPublicVar: "foo",myPublicFunction: function (bar) {

myPrivateVar++;myPrivateMethod(bar);

}};

})();

Loose Augumentation:

var MODULE = (function (my) { // add capabilities...return my;

}(MODULE || {}));

http://www.adequatelygood.com/2010/3/JavaScript-Module-Pattern-In-Depth

Cloning & Inheritance

for (key in old) { if (old.hasOwnProperty(key)) { my[key] = old[key]; } }

Option 1: var myApplication = myApplication || {};Option 2 if(!MyApplication) MyApplication = {};Option 3: var myApplication = myApplication = myApplication || {}Option 4: myApplication || (myApplication = {});Option 5: var myApplication = myApplication === undefined ? {} : myApplication;

Page 10: Design patterns in javascript

The Revealing Module Patternvar myRevealingModule = (function(){

var name = 'John Smith';function setPerson (personName) {

name = personName;}function getPerson () {

return name;}return {

// set: setPerson,get: getPerson

};}());

Advantage: same style

Disadvantage: when over-writting

Page 11: Design patterns in javascript

Observer Pattern & Mediator Patternvar pubsub = {};(function(q) { var topics = {}, subUid = -1; q.publish = function( topic, args ) { if ( !topics[topic] ) { return false; } var subscribers = topics[topic], var tempArr; if(subscribers ) { tempArr = subscribers .slice(0); while (tempArr .length) { tempArr.pop().func(topic, args); } } return this; };

q.subscribe = function( topic, func ) { if (!topics[topic]) { topics[topic] = []; } var token = (++subUid).toString(); topics[topic].push({ token: token, func: func }); return token; };

q.unsubscribe = function( token ) { for ( var m in topics ) { if ( topics[m] ) { for (var i = 0, j = topics[m].length; i < j; i++) { if (topics[m][i].token === token) { topics[m].splice(i, 1); return token; } } } } return this; };}( pubsub ));

Mediator Pattern: Colleague – communicate by -> Mediator

Observer - listener to -> Mediator

Backbone.js: listen to changes in model

Page 12: Design patterns in javascript

Mediator:Communication between Colleague

Façade: N-to-1, listener to a event hub

Page 13: Design patterns in javascript

Command Pattern(function(){ var CarManager = { requestInfo: function( model, id ){}, buyVehicle: function( model, id ){}, arrangeViewing: function( model, id ){} };})();

CarManager.execute = function (name) { // we can log the commands here, also implements an undo function return CarManager[name] && CarManager[name].apply(CarManager, [].slice.call(arguments, 1));};

CarManager.execute("arrangeViewing", "Ferrari", "14523");CarManager.execute("requestInfo", "Ford Escort", "34232");CarManager.execute("buyVehicle", "Ford Escort", "34232");

We can keep track of the commands, also record a game reply

Page 14: Design patterns in javascript

Façade Patternvar foo = document.getElementById('foo'); foo.style.color = 'red'; foo.style.width = '150px'; var bar = document.getElementById('bar'); bar.style.color = 'red'; bar.style.width = '150px'; var baz = document.getElementById('baz'); baz.style.color = 'red'; baz.style.width = '150px';

function setStyles(elements, styles) { for (var i=0, length = elements.length; i < length; i++) { var element = document.getElementById(elements[i]); for (var property in styles) { element.style[property] = styles[property]; } }} //Now you can just write this:setStyles(['foo', 'bar', 'baz'], { color: 'red', width: '150px'});

Very common in Jquery: .css(), .animate()…

Page 15: Design patterns in javascript

Factory Patternvar AbstractVehicleFactory = (function () {

var types = {};return {

getVehicle: function (type, customizations) {// we can keep track of vehicle or get vehicle by options instead of type …var Vehicle = types[type];return (Vehicle) ? return new Vehicle(customizations) : null;

},registerVehicle: function (type, Vehicle) {

var proto = Vehicle.prototype;if (proto.drive && proto.breakDown) {

types[type] = Vehicle;}return AbstractVehicleFactory;

}};

})();

AbstractVehicleFactory.registerVehicle("car", Car);AbstractVehicleFactory.registerVehicle("truck", Truck);var car = AbstractVehicleFactory.getVehicle("car", { color: "yellow", turbo: true });var truck = AbstractVehicleFactory.getVehicle("truck", { monster: true, cylinders: 12 });

Give me a android phone with 4.1 inch screen around S$400

Page 16: Design patterns in javascript

Mixin Patternfunction augment( receivingClass, givingClass ) { if ( arguments[2] ) { for (var i=2, len=arguments.length; i<len; i++) { receivingClass.prototype[arguments[i]] = givingClass.prototype[arguments[i]]; } } else { for ( var methodName in givingClass.prototype ) { if ( !receivingClass.prototype[methodName] ) { receivingClass.prototype[methodName] = givingClass.prototype[methodName]; } } }}

var Car = function( settings ){this.model = settings.model ;this.colour = settings.colour ;

};

var Mixin = function(){};Mixin.prototype = {

driveForward: function(){},driveBackward: function(){}

};

augment( Car, Mixin,'driveForward','driveBackward' );

var vehicle = new Car({model:'Ford', colour:'blue'});

vehicle.driveForward();vehicle.driveBackward();

We have update our factory to producetanks with new weapon

Class level

Page 17: Design patterns in javascript

Decorator Pattern

• SubClassingvar Person = function( firstName , lastName ){

this.firstName = firstName;this.lastName = lastName;this.gender = 'male'

};

var Superhero = function( firstName, lastName , powers ){Person.call(this, firstName, lastName);this.powers = powers;

}SuperHero.prototype = Object.create(Person.prototype);

var superman = new Superhero( "Clark" ,"Kent" , ['flight','heat-vision'] );

Object level

Page 18: Design patterns in javascript

function Vehicle( vehicleType ){ this.vehicleType = vehicleType || 'car', this.model = 'default', this.license = '00000-000'}function makeTruck(truck){ truck.setModel =function( modelName ){} truck.setColor = function( color ){} return truck;}var truck = makeTruck(new Vehicle());

function MacBook() { this.cost = function () { return 997; }; this.screenSize = function () { return 13.3; };}

function Memory( macbook ) { var v = macbook.cost(); macbook.cost = function() { return v + 75 ;}}function LargeScreen( macbook ){ var v = macbook.cost(); macbook.cost = function(){ return v + 100; }; macbook.screenSize=function(){ return 14.8; };}function Insurance( macbook ){var v = macbook.cost(); macbook.cost = function(){ return v + 250; };}

var mb = new MacBook();Memory(mb);LargeScreen(mb);Insurance(mb);

• Simple decorator • Mulit decorator

user can wear equips to change attrand gain new ablitities

Page 19: Design patterns in javascript

Pseudo-classical decorators: interfacevar TodoList = new Interface('Composite', ['add', 'remove']);var TodoItem = new Interface('TodoItem', ['save']);

var myTodoList = function(id, method, action) { // implements TodoList, TodoItem};

function addTodo( todoInstance ) { Interface.ensureImplements(todoInstance, TodoList, TodoItem); // …}

https://gist.github.com/1057989var Interface = function(name, methods){…}Interface.ensureImplements = function(obj){…}

Make sure that only archers can enter certain stage

Page 20: Design patterns in javascript

Flyweight patternvar Book = function(){ this.id = id; this.title = title; this.author = author; this.genre = genre; this.pageCount = pageCount; this.publisherID = publisherID; this.ISBN = ISBN; this.checkoutDate = checkoutDate; this.checkoutMember = checkoutMember; this.dueReturnDate = dueReturnDate; this.availability = availability;};

Book info:var Book = function () { this.title = title; this.author = author; this.genre = genre; this.pageCount = pageCount; this.publisherID = publisherID; this.ISBN = ISBN;};

Share date as much as possible

Checkout Info:

Page 21: Design patterns in javascript

var BookFactory = (function () { var existingBooks = {}; return { createBook: function () { var existingBook = existingBooks[ISBN]; if (existingBook) { return existingBook; } else { var book = new Book(); existingBooks[ISBN] = book; return book; } } }});

var BookRecordManager = (function () { var bookRecordDatabase = {}; return {

addBookRecord: function ( var book = bookFactory.createBook(); bookRecordDatabase[id] = { checkoutMember: checkoutMember, checkoutDate: checkoutDate, dueReturnDate: dueReturnDate, availability: availability, book: book }; },

updateCheckoutStatus: function () {}, extendCheckoutPeriod: function () {}, isPastDue: function (bookID) {} };});

Page 22: Design patterns in javascript

Object PoolPool = {arr:[]};

Pool.pop = function(){ if(Pool.arr.length > 0){ return Pool.arr.pop().wake(); }else{ return new Obj(); }}Pool.push= function(obj){ Pool.arr.push(obj); obj.sleep();}Object pools are used to avoid the instantiation cost of creating new objects by re-using existing ones. Avoid creating objects is gc-friendly.

May have hundreds of planes through the whole game, but not more than 20 at the same time.

Generator a plan: pop()Leave screen or destroyed: push()

Reuse resource when it has a high cost to make or release, eg. connection pool

Page 23: Design patterns in javascript

MV* pattern• V: Templing

Handlebars, Underscore…• *: bind event, route, render, data-binding… • C: flex, extendable, suit for big project

Backbone, Spine…• P: Present Model -> View, view no direct access to model, safer

then MVC Backbone , Angular…

• VM: VM & V more tightly, suit small project Knockout…

TODO MVC http://todomvc.com/

Page 24: Design patterns in javascript

Namespace• Automating nested namespacing

var application = { utilities:{ drawing:{ canvas:{ 2d:{} } } }};

// automatic namespacingvar myApp = myApp || {};

function extend( ns, ns_string ) { var parts = ns_string.split('.'), parent = ns, pl, i; pl = parts.length; for (i = 0; i < pl; i++) { if (typeof parent[parts[i]] == 'undefined') { parent[parts[i]] = {}; } parent = parent[parts[i]]; } return parent;}

var mod = extend(myApp, 'myApp.modules.module2');

Page 25: Design patterns in javascript

• Deep object extensionfunction extend(destination, source) { var toString = Object.prototype.toString, objTest = toString.call({}); for (var property in source) { if (source[property] && objTest == toString.call(source[property])) { destination[property] = destination[property] || {}; extend(destination[property], source[property]); } else { destination[property] = source[property]; } } return destination;};

Jquery.extend

Page 26: Design patterns in javascript

Design pattern in JQuery core• Adapter pattern

.css({opacity:.5})• Façade pattern

.ajax()• Observe pattern

.on(event, cssSelector, func)• Iterator pattern

array is also object• Proxy pattern

.proxy(function(){this…}, thisObj) control before sth really loaded • Builder Pattern

$(“<a>a link</a>”).appendTo(…) xml initing

Page 27: Design patterns in javascript

Modular JS

• AMD• CMD• Harmony

Script loaders

Page 28: Design patterns in javascript

AMD• Asynchronous Module Definition, Commonjs• RequireJS• define(moduleID, [dependencies], definition function)• require([dependencies], function)

https://github.com/amdjs/amdjs-api/wiki/AMD

Page 29: Design patterns in javascript

CMD

• require()• Exports

cmd: Common Module Definition

Sea.js

• Modules are singletons.• New free variables within the module scope should not be introduced.• Execution must be lazy, import can be later: define(dependency, …) vs.

define(function(require(…))• With some care, modules may have cyclic dependencies.

define(function(require, exports, module) { // The module code goes here });

http://wiki.commonjs.org/wiki/Modules/Wrappings

Page 30: Design patterns in javascript

harmony

• Export• Import• Module