Sie sind auf Seite 1von 75

Design Patterns in JavaScript

Presentation by:
ADMEC Multimedia Institute
Topics to Cover

- Basics of Object Oriented Programming


- Why We Need OOJS and Design Patterns
- Differences Between OOJS and
Design Patterns
- Design Pattern Types and their Uses
- Design Patterns that jQuery uses
What is Design Pattern?

Design pattern is a reusable solution that


can be applied to commonly occurring
problems in software design – in our case,
in writing JavaScript web applications.
Why Design Patterns?

Design patterns have three main benefits:


● Patterns are proven solutions
● Patterns can be easily reused
● Patterns can be expressive
●Patterns are proven solutions

They provide solid


approaches to solving issues
in software development
using proven techniques that
reflect the experience and
insights of the developers that
helped in defining them bring
to the pattern.
●Patterns can be easily reused

A pattern usually reflects an out-of-the-box


solution that can be adapted to suit our own
needs.
This feature makes them
quite robust.
●Patterns can be expressive

When we look at a pattern,


there's generally a set
structure and vocabulary
to the solution presented
that can help express
rather large solutions
quite elegantly.
We already use patterns everyday

Imagine we want to select some html elements


with class .foo.
● jQuery approach
● querySelectorAll()
● getElementsByClassName()
jQuery uses facade pattern to solve this issue.
Categories of Design Patterns

Creational Design Patterns


------------------------------------------------
Structural Design Patterns
------------------------------------------------
Behavioral Design Patterns
●Creational Design Patterns

● These patterns focus on handling object


creation mechanisms where objects are created
in a manner suitable for a given situation.
● Examples: Constructor, Factory, Abstract,
Prototype, Singleton, and Builder
●Structural Design Patterns

● These patterns are concerned with object


composition and typically identify simple ways to
realize relationships between different objects.
● They help in ensuring that when one part of a
system changes, the entire structure of the system
doesn't need to do the same.
● Examples: Decorator, Facade, Flyweight,
Adapter, and Proxy.
●Behavioral Design Patterns

● Behavioral patterns focus on improving or


streamlining the communication between
objects in a system.
● Examples: Iterator, Mediator, Observer, and
Visitor.
Explaining all type of
Design Patterns in
JavaScript
The Constructor Pattern

● In classical object oriented programming


languages, a constructor is a special method
used to initialize a newly created object once
memory has been allocated for it.
● var newObject = {};
● var newObject = new Object();
● or any custom object instantiation
The Constructor Pattern Example

function Car( model, year, miles ) {


this.model = model;
this.year = year;
this.miles = miles;
this.toString = function () {
return this.model + " has done " + this.miles + " miles";
}
}
// Usage
// We can create new instances of the car
The Constructor Pattern Example

var civic = new Car( "Honda Civic", 2009, 20000 );


var mondeo = new Car( "Ford Mondeo", 2010, 5000 );
// and then open our browser console to view the
// output of the toString() method being called on
// these objects

console.log( civic.toString() );
console.log( mondeo.toString() );
The Module Pattern

● Modules are an integral piece of any robust


application's architecture and typically help in
keeping the units of code for a project both cleanly
separated and organized.
● Module pattern was originally defined as a way to
provide both private and public encapsulation for
classes in conventional software engineering.
The Module Pattern Example

var testModule = (function () {


var counter = 0;
return {
incrementCounter: function () {
return counter++;
},
resetCounter: function () {
console.log( "counter value prior to reset: " + counter );
counter = 0;
}
};
})();
The Module Pattern Example

// Usage:
// Increment our counter
testModule.incrementCounter();
// Check the counter value and reset
// Outputs: counter value prior to reset: 1
testModule.resetCounter();
The Module Pattern Example

Here, other parts of the code are unable to directly read the value
of our incrementCounter() or resetCounter(). The counter variable
is actually fully shielded from our global scope so it acts just like a
private variable would - its existence is limited to within the
module's closure so that the only code able to access its scope are
our two functions.
Our methods are effectively namespaced so in the test section of
our code, we need to prefix any calls with the name of the module
(e.g. "testModule").
The Singleton Pattern

● It is thus known because it restricts instantiation of a


class to a single object.
● Classically, it can be implemented by creating a class
with a method that creates a new instance of the class
if one doesn't exist.
● It differs from static classes as we can delay their
initialization, generally because they require some
information that may not be available during
initialization time.
The Singleton Pattern Example

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";
var privateRandomNumber = Math.random();
return {
The Singleton Pattern Example

// Public methods and variables


publicMethod: function () {
console.log( "The public can see me!" );
},
publicProperty: "I am also public",
getRandomNumber: function() {
return privateRandomNumber;
}
};
};
return {
The Singleton Pattern Example

// Get the Singleton instance if one exists or create one if it doesn't


getInstance: function () {
if ( !instance ) {
instance = init();
}
return instance;
}
};
})();
var myBadSingleton = (function () {
// Instance stores a reference to the Singleton
var instance;
The Singleton Pattern Example

function init() {
// Singleton
var privateRandomNumber = Math.random();
return {
getRandomNumber: function() {
return privateRandomNumber;
}
};
};
return {
// Always create a new Singleton instance
getInstance: function () {
instance = init();
return instance;
The Singleton Pattern Example

}
};

})();

// Usage:

var singleA = mySingleton.getInstance();

var singleB = mySingleton.getInstance();

console.log( singleA.getRandomNumber() === singleB.getRandomNumber() ); // true

var badSingleA = myBadSingleton.getInstance();

var badSingleB = myBadSingleton.getInstance();

console.log( badSingleA.getRandomNumber() !== badSingleB.getRandomNumber() ); // true


The Singleton Pattern Example

// Note: as we are working with random numbers, there is a

// mathematical possibility both numbers will be the same,

// however unlikely. The above example should otherwise still

// be valid.

Note:
What makes the Singleton is the global access to the instance (generally
through MySingleton.getInstance()) as we don't (at least in static
languages) call new MySingleton() directly.
The Observer Pattern

● It is a design pattern in which an object maintains a


list of objects depending on it (observers),
automatically notifying them of any changes to
state.
The Observer Pattern Example

We can now expand on what we've learned to implement the Observer pattern with the following
components:

Subject: maintains a list of observers, facilitates adding or removing observers

Observer: provides a update interface for objects that need to be notified of a Subject's changes of
state

ConcreteSubject: broadcasts notifications to observers on changes of state, stores the state of


ConcreteObservers

ConcreteObserver: stores a reference to the ConcreteSubject, implements an update interface for


the Observer to ensure state is consistent with the Subject's

First, let's model the list of dependent Observers a subject may have:

function ObserverList(){
this.observerList = [];
}
The Observer Pattern Example

ObserverList.prototype.add = function( obj ){


return this.observerList.push( obj );
};
ObserverList.prototype.count = function(){
return this.observerList.length;
};
ObserverList.prototype.get = function( index ){
if( index > -1 && index < this.observerList.length ){
return this.observerList[ index ];
}
};
ObserverList.prototype.indexOf = function( obj, startIndex ){
var i = startIndex;
The Observer Pattern Example

while( i < this.observerList.length ){


if( this.observerList[i] === obj ){
return i;
}

i++;
}

return -1;

};

ObserverList.prototype.removeAt = function( index ){


this.observerList.splice( index, 1 );

};

Next, let's model the Subject and the ability to add, remove or notify observers on
the observer list.
The Observer Pattern Example

function Subject(){

this.observers = new ObserverList();

Subject.prototype.addObserver = function( observer ){

this.observers.add( observer );

};

Subject.prototype.removeObserver = function( observer ){

this.observers.removeAt( this.observers.indexOf( observer, 0 ) );

};

Subject.prototype.notify = function( context ){

var observerCount = this.observers.count();

for(var i=0; i < observerCount; i++){


The Observer Pattern Example

this.observers.get(i).update( context );

};

We then define a skeleton for creating new Observers. The update functionality here will be overwritten later with
custom behaviour.

// The Observer

function Observer(){

this.update = function(){

// ...

};

In our sample application using the above Observer components, we now define:
The Observer Pattern Example

● A button for adding new observable checkboxes to the page


● A control checkbox which will act as a subject, notifying other checkboxes they should be checked
● A container for the new checkboxes being added

We then define ConcreteSubject and ConcreteObserver handlers


for both adding new observers to the page and implementing the
updating interface. See below for inline comments on what these
components do in the context of our example.
HTML:

<button id="addNewObserver">Add New Observer checkbox</button>

<input id="mainCheckbox" type="checkbox"/>


<div id="observersContainer"></div>
The Observer Pattern Example

Sample script:

// Extend an object with an extension

function extend( obj, extension ){

for ( var key in extension ){

obj[key] = extension[key];

// References to our DOM elements

var controlCheckbox = document.getElementById( "mainCheckbox" ),

addBtn = document.getElementById( "addNewObserver" ),

container = document.getElementById( "observersContainer" );


The Observer Pattern Example

// Concrete Subject

// Extend the controlling checkbox with the Subject class

extend( controlCheckbox, new Subject() );

// Clicking the checkbox will trigger notifications to its observers

controlCheckbox.onclick = function(){

controlCheckbox.notify( controlCheckbox.checked );

};

addBtn.onclick = addNewObserver;
The Observer Pattern Example

// Concrete Observer

function addNewObserver(){

// Create a new checkbox to be added

var check = document.createElement( "input" );

check.type = "checkbox";

// Extend the checkbox with the Observer class

extend( check, new Observer() );

// Override with custom update behaviour

check.update = function( value ){

this.checked = value;

};
The Observer Pattern Example

// Add the new observer to our list of observers

// for our main subject

controlCheckbox.addObserver( check );

//append the item to the container


container.appendChild( check );
}

In this example, we looked at how to implement and utilize the


Observer pattern, covering the concepts of a Subject, Observer,
ConcreteSubject and ConcreteObserver.
The Mediator Pattern

● It is a behavioral design pattern that allows us


to expose a unified interface through which the
different parts of a system may communicate.
● A tower (mediator) and planes example
The Mediator Pattern

● It is a behavioral design pattern that allows us


to expose a unified interface through which the
different parts of a system may communicate.
● A tower (mediator) and planes example
The Mediator Pattern Example

A simple implementation of the Mediator pattern can be found below, exposing both publish() and
subscribe() methods for use:

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;

};
The Mediator Pattern Example

//Publish/broadcast an evevt to the rest of the application

var publish = function( topic ){

var args;

if ( !topics[topic] ){

return false;

args = Array.prototype.slice.( arguments, 1 );

for ( var I =0, 1= topics [topic].length; I < 1; i++ ) {

var subscription = topics[topic][i];

subscription.callback.apply( subscription.context, args );

return this;
The Mediator Pattern Example

};

return {

Publish: publish,

Subscribe: subscribe,

installTo: function( obj ){

obj.subscibe = subscribe;

obj.publish = publish;

};

}() );
The Prototype Pattern

● Prototype pattern as one that creates objects based


on a template of an existing object through cloning.
● It is probably based on prototypical inheritance in
which we create objects that act as prototypes for
other objects.
● The prototype object itself is effectively used as a
blueprint for each object the constructor creates.
The Prototype Pattern Example

We can see this demonstrated in the example below:


var myCar = {

name: "Ford Escort",

drive: function () {

console.log( "Weeee. I'm driving!" );

},

panic: function () {

console.log( "Wait. How do you stop this thing?" );

};

// Use Object.create to instantiate a new car

var yourCar = Object.create( myCar );


The Prototype Pattern Example

// Now we can see that one is a prototype of the other

console.log( yourCar.name );

Object.create also allows us to easily implement advanced concepts such as differential inheritance
where objects are able to directly inherit from other objects. We saw earlier that Object.create allows us to
initialise object properties using the second supplied argument. For example:

var vehicle = {

getModel: function () {

console.log( "The model of this vehicle is.." + this.model );

};

var car = Object.create(vehicle, {

"id": {

value: MY_GLOBAL.nextId(),
The Prototype Pattern Example

// writable:false, configurable:false by default

enumerable: true

},

"model": {

value: "Ford",

enumerable: true

});

Here the properties can be initialized on the second argument of Object.create using an object literal with
a syntax similar to that used by the Object.defineProperties and Object.defineProperty methods that we
looked at previously.

It is worth noting that prototypal relationships can cause trouble when enumerating properties of objects
and (as Crockford recommends) wrapping the contents of the loop in a hasOwnProperty() check.
The Prototype Pattern Example

If we wish to implement the prototype pattern without directly using Object.create, we can simulate the
pattern as per the above example as follows:

var vehiclePrototype = {

init: function ( carModel ) {

this.model = carModel;

},

getModel: function () {

console.log( "The model of this vehicle is.." + this.model);

};

function vehicle( model ) {

function F() {};

F.prototype = vehiclePrototype;
The Prototype Pattern Example

var f = new F();

f.init( model );

return f;

var car = vehicle( "Ford Escort" );

car.getModel();

Note: This alternative does not allow the user to define read-only properties in the same manner (as the
vehiclePrototype may be altered if not careful).

A final alternative implementation of the Prototype pattern could be the following:

var beget = (function () {

function F() {}

return function ( proto ) {

F.prototype = proto;
The Prototype Pattern Example

return new F();

};

})();

One could reference this method from the vehicle


function. Note, however that vehicle here is emulating
a constructor, since the prototype pattern does not
include any notion of initialization beyond linking an
object to a prototype.
The Facade Pattern

● Facades are structural pattern that can often be


seen in jQuery e.g. $(ele).css() or .animate()
● When we put up a Facade, we present an outward
appearance to the world that may conceal a very
different reality.
● This pattern provides a higher level interface to a
larger body of code, hiding its true underlying
complexity.
The Facade Pattern Example

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;

};
Factory Pattern

● It is another creational pattern concerned with the notion of


creating objects.
● Where it differs from the other patterns in its category is
that it doesn't explicitly require the use of a constructor.
● It is useful if the object creation process is relatively
complex e.g., if it strongly depends on dynamic factors or
application configuration.
● Example of this pattern can be found in UI libraries such
as ExtJS, where the methods for creating objects or
components may be further subclassed.
The Factory Pattern Example

The following is an example that builds upon our previous snippets using the Constructor pattern logic to
define cars. It demonstrates how a Vehicle Factory may be implemented using the Factory pattern:

// 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){


The Factory Pattern Example

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 ) {


The Factory Pattern Example

switch(options.vehicleType){

case "car":

this.vehicleClass = Car;

break;

case "truck":

this.vehicleClass = Truck;

break;

//defaults to VehicleFactory.prototype.vehicleClass (Car)

return new this.vehicleClass( options );

};
The Factory Pattern Example

// 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 );
The Factory Pattern Example

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 );
The Factory Pattern Example

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 );


The Factory Pattern Example

// Outputs: Truck object with the color "pink", wheelSize "so big"

// and state "omg. so bad"

console.log( myBigTruck );

When To Use The Factory Pattern


The Factory pattern can be especially useful when applied to the following situations:
● When our object or component setup involves a high level of complexity
● When we need to easily generate different instances of objects depending on the environment we are in
● When we're working with many small objects or components that share the same properties
● When composing objects with instances of other objects that need only satisfy an API contract (aka, duck
typing) to work. This is useful for decoupling.
The Factory Pattern Example

When Not To Use The Factory Pattern


When applied to the wrong type of problem, this pattern can
introduce an unnecessarily great deal of complexity to an
application. Unless providing an interface for object creation
is a design goal for the library or framework we are writing, I
would suggest sticking to explicit constructors to avoid the
unnecessary overhead.
Due to the fact that the process of object creation is
effectively abstracted behind an interface, this can also
introduce problems with unit testing depending on just how
complex this process might be.
The Decorator Pattern

● It is a structural design pattern that aim to promote


code reuse. It is useful for object sub classing.
● Classically, Decorators offered the ability to add
behavior to existing classes in a system dynamically.
● Very useful when applications may contain features
requiring a large quantity of distinct types of object e.g.
a JavaScript game.
● $.extend() allows you to add features dynamically.
The object constructors could represent distinct player types, each with differing
capabilities. A Lord of the Rings game could require constructors for Hobbit, Elf, Orc,
Wizard, Mountain Giant, Stone Giant and so on, but there could easily be hundreds of
these. If we then factored in capabilities, imagine having to create sub-classes for each
combination of capability type e.g HobbitWithRing,HobbitWithSword,
HobbitWithRingAndSword and so on.This isn't very practical and certainly isn't
manageable when we factor in a growing number of different abilities.

The Decorator pattern isn't heavily tied to how objects are created but instead focuses on
the problem of extending their functionality. Rather than just relying on prototypal
inheritance, we work with a single base object and progressively add decorator objects
which provide the additional capabilities. The idea is that rather than sub-classing, we
add (decorate) properties or methods to a base object so it's a little more streamlined.

A very simplistic decorator may be implemented as follows:


Example 1: 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

This type of simplistic implementation is functional, but it doesn't really demonstrate all of
the strengths Decorators have to offer. For this, we're first going to go through my
variation of the Coffee example from an excellent book called Head First Design Patterns
by Freeman, Sierra and Bates, which is modeled around a Macbook purchase.
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() );
In the above example, our Decorators are overriding the MacBook() super-class
objects .cost() function to return the current price of the Macbook plus the cost
of the upgrade being specified.
It's considered a decoration as the original Macbook objects constructor
methods which are not overridden (e.g. screenSize()) as well as any other
properties which we may define as a part of the Macbook remain unchanged
and intact.
There isn't really a defined interface in the above example and we're shifting
away the responsibility of ensuring an object meets an interface when moving
from the creator to the receiver.
JavaScript MV* Patterns

● It is very important architectural pattern:


– MVC – Model View Controller
– MVP – Model View Presenter
– MVVM – Model View ViewModel
● Libraries which use it: Backbone.js (MVP), Ember.js
and AngularJS(MVC)
● What are all components in MVC?
JavaScript developer understand what this pattern provides.
This allows us to effectively appreciate what these frameworks
enable us to do differently.
We know that MVC is composed of three core components:
Views:-
Views are a visual representation of models that present a
filtered view of their current state. Whilst Smalltalk views are
about painting and maintaining a bitmap, JavaScript views are
about building and maintaining a DOM element.
A view typically observes a model and is notified when the model
changes, allowing the view to update itself accordingly. Design
pattern literature commonly refers to views as "dumb" given that
their knowledge of models and controllers in an application is
limited.
Users are able to interact with views and this includes the ability to
read and edit (i.e get or set the attribute values in) models. As the
view is the presentation layer, we generally present the ability to
edit and update in a user-friendly fashion. For example, in the
former photo gallery application we discussed earlier, model
editing could be facilitated through an "edit' view where a user who
has selected a specific photo could edit its meta-data.
Design Patterns in jQuery

● It is currently the most popular JavaScript DOM


manipulation library and provides an abstracted layer
for interacting with the DOM in a safe, cross-browser
manner. The simplicity behind the jQuery came in
because of the design patterns that it uses.
● Composite | Adapter | Facade | Observer | Iterator |
Lazy Initialization | Proxy | Builder
Thank You

Special thanks to

Learning JavaScript Design Patterns


(Addy Osmani) O'Reilly
YouTube | Google

ADMEC MULTIMEDIA INSTITUTE


C-7/114, IInd Floor, Sector-7, Rohini, Delhi, India
website: http://www.admecindia.co.in
phones: 9811-8181-22, 9811-8181-22
Twitter: @admecinstitute

Das könnte Ihnen auch gefallen