Javascript Common Design Patterns

Post on 07-Aug-2015

455 views 3 download

transcript

JavaScript Design Patterns

2014 – Pham Tung

Contents

• What is Design Pattern?• Why Design Pattern?• Types of Design Pattern• Explore some patterns

1. Singleton Pattern2. Factory Pattern3. Module Pattern4. Decorator Pattern5. Command Pattern6. Observer Pattern

What is Design Pattern

• A general reusable solution to a commonly occurring problem in software design.

• Not a finished design that can be transformed directly into code. It is a description or template for how to solve a problem that can be used in many different situations.

Why Design Patterns

Building diagram?No need!

Why Design Patterns

Why Design Patterns

• To design a new software system quickly and efficiently.

• To understand a existing software system.• Speed up the development process by providing

tested, proven development paradigms.• Help to prevent subtle issues that can cause major

problems, code readability.• Allow developers to communicate using well-

known, well understood names for software interactions.

Types of Design Pattern

• Creational– Object creation.

• Structural– Relationship between entities.

• Behavioral– Communication between objects.

Periodic Table of Patterns

Patterns we are going to explore

Creational

● Singleton

● Factory

Structural

● Decorator

Behavioral

● Command

● Observer

● Module

Singleton Pattern

Creational:

1

Singleton Pattern

• Ensure a class has only one instance, and provide a global point of access to it.

• Encapsulated “just-in-time initialization” or “initialization on first use”.

• Reduce the need for global variables which is particularly important in JavaScript because it limits namespace pollution and associated risk of name collisions.

• The Module pattern is JavaScript's manifestation of the Singleton pattern.

Singleton Pattern1. var Singleton = (function () {2. var instance;3. function createInstance() {4. var object = new Object("I am the instance");5. return object;6. }7. return {8. getInstance: function () {9. if (!instance) {10. instance = createInstance();11. }12. return instance;13. }14. };15. })();16. var instance1 = Singleton.getInstance();17. var instance2 = Singleton.getInstance();18. alert("Same instance? " + (instance1 === instance2));

Singleton Pattern1. function MySingletonClass() {

2. if ( arguments.callee._singletonInstance )3. return arguments.callee._singletonInstance;4. arguments.callee._singletonInstance = this;

5. this.Foo = function() {6. // ...7. }8. }

9. var a = new MySingletonClass()10. var b = MySingletonClass()

11. console.log( a === b ); // prints: true

Factory Patterns

Creational:

2

Factory Patterns

Classes should be open for extension, but closed for modification.

Dependency Inversion Principle

Depend upon abstractions. Do not depend upon concrete classes.

Factory Patterns

● Simple Factory

● Factory Method

● Abstract Factory

Factory Patterns

I want to buy a Doraemon book.

Oh, yes yes! Please send me that Rabbit book.

Factory Patterns

Factory Patterns

Factory Patterns

OMG!

IS IT FUNNY!

Factory Patterns(Simple Factory)

● Is a class with a public static method which will actually do the object creation task according to the input it gets.

Factory Patterns(Simple Factory)

function orderBook(var type){ var book;

if (type === 'comic') {book = new

ComicBook();} else if (type === 'history') {

book = new HistoryBook();} else if (type === 'Science') {

book = new ScienceBook();}

book.prepare();book.box();return book;

}

We need to modify the entire code whenever a new type is added.

And this code is consistent. We don’t need to make a change.

But only this highlight code need to be modified.

Factory Patterns(Simple Factory)

function orderBook(type){ var book;

book.prepare();book.box();return book;

}

var Factory = {getBook : function (type){

var book;if (type === 'comic') {

book = new ComicBook();

} else if (type === 'history') {book = new

HistoryBook();} else if (type === 'Science') {

book = new ScienceBook();

}return book;

}};

Factory.getBook("comic");

book = Factory.getBook(type)

So, why don’t we move this mess to another place?

Factory Patterns(Factory Method Pattern)

• Define an interface for creating an object, but let subclasses decide which class to instantiate. Factory lets a class defer instantiation to subclasses.

Factory Patterns(Factory Method Pattern)

Factory Patterns(Factory Method Pattern)

function BookFactory(){this.getBook = function (type){

var book;if (type === 'comic') {

book = new ComicBook();

} else if (type === 'hisoty') {book = new

HistoryBook();} else if …

return book;}

}

var factory = new BookFactory();var book = factory.getBook( 'comic');

Factory Patterns(Abstract Factory Pattern)

• Abstract Factory offers the interface for creating a family of related objects, without explicitly specifying their classes.

Factory Patterns(Abstract Factory Pattern)

Factory Patterns(Abstract Factory Pattern)

Factory Patterns(Abstract Factory Pattern)

function EnglishBookFactory(){this.getBook = function(type){

// …return book;

};this.getNewspaper = function(){

// … return newspaper;

};}function VietnameseBookFactory(){

// …}

var factory = new EnglishBookFactory();var store = new XyzBookStore(factory);store.comeIntoOperations();

Factory Patterns

Create only one product.

Exposes a method to the client for creating the object.

Factory Method Abstract Factory

Creating families of related or dependent products.

Expose a family of related objects which may consist of these Factory methods.

2

1

Factory Patterns

Hides the construction of single object.

Uses inheritance and relies on derived class or sub class to create object.

Hides the construction of a family of related objects.

Uses composition to delegate responsibility of creating object to another class.

Factory Method Abstract Factory

3

4

Factory Patterns

Differences (when to use):● Factory Method

○ There is a need to decouple a client from a particular product that it uses

○ To relieve a client of responsibility for creating and configuring instances of a product.

● Abstract Factory○ Clients must be decoupled from product classes.

Especially useful for program configuration and modification.

○ Can also enforce constraints about which classes must be used with others.

Factory Patterns

• When To Use:– When your object's setup requires a high level of

complexity.– When you need to generate different instances

depending on the environment.– When you're working with many small objects that share

the same properties.– When composing classes with instances of other classes

that need only satisfy an API contract (aka, duck typing) to work. This is useful for decoupling.

Module Pattern

Creational, Structural:

3

Module Pattern

function

private members

public members

object

Module Pattern

function

private members

public members

objectreturn

Module Pattern

public members

object

Module

Module Pattern

public members

Module

Closure

private members

Module Pattern

• Provide both private and public encapsulation for classes.

• Emulate the concept of classes: able to include both public/private methods and variables inside a single object, thus shielding particular parts from the global scope. ☛ Reduce names conflicting with other functions defined in additional scripts on the page.

Module Pattern1. var TestModule = (function () {2. var counter = 0; // private member3. return {4. incrementCounter: function () {5. return counter++;6. },7. resetCounter: function () {8. console.log('counter value prior to reset:' + counter);9. counter = 0;10. }11. };12. })();13. // test14. TestModule.incrementCounter();15. TestModule.resetCounter();

Module Pattern

testModule.printCounter = function(){console.log(counter);

};

1. var testModule = (function () {2. var counter = 0; // private member3. return {4. incrementCounter: function () { ... },5. resetCounter: function () { ... }6. };7. })();

What will happen if I dynamically add a new function to the testModule variable.

Like this.

Module Pattern

WTF

testModule.printCounter = function(){console.log(counter);

};testModule.printCounter();

Module Pattern• Advantages

– Cleaner for developers coming from an object-oriented background than the idea of true encapsulation.

– Supports private data.• Disadvantages

– Hard to change visibility of members.– Can't access private members in methods that are added

to the object at a later point (dynamically add method).– Inability to create automated unit tests for private

members and additional complexity.

Decorator Pattern

Structural:

4

Decorator Pattern

Classes should be open for extension, but closed for modification.

Open-Closed Principle

Classes should be open for extension, but closed for modification.

Decorator Pattern

5 $

7 $

DecoratorComponent

Decorator Pattern7 $

Decorator Pattern7 $

Decorator Pattern7 $

Waiter: NOT BAD!LIKE A BOSS

Decorator Pattern99 $

29 $

89 $7 $

Waiter: Here is your bill, sir

7 + 89 + 99 + 29 = 224 $

Decorator Pattern

WormChickenRice

WormRice

ChickenWormRice

EggRice

WormEggRice

ChickenEggRice

ScorpionEggRice

ChickenWormEggRice

ScorpionRice

BeerChickenEggRice

ChickenEggRice

BeerEggRice

ChickenScorpionRice

Way 1:

Decorator Pattern

Beer (+ 29$)

Chicken (+99$)

Worm (+89$)

Rice (7$)

Way 2:

Decorator Pattern

• Attach additional responsibilities to an object dynamically. Decorators provide a flexible alternative to subclassing for extending functionality.

• Multiple decorators can add or override functionality to the original object.

Decorator Pattern

Decorator Patternfunction Rice(){ this.cost = 7;}

function WormDecorator(rice){ rice.cost += 89; }

function ChickenDecorator(rice){ rice.cost += 99;}

function BeerDecorator(rice){ rice.cost += 29;}

var rice = new Rice(); WormDecorator(rice); ChickenDecorator(rice); BeerDecorator(rice);alert(rice.cost);// 224

Command Pattern

Behavioral:

5

Command Pattern

• Encapsulate requests/actions as objects.• Command objects allow for loosely coupled

systems by separating the objects that issue a request from the objects that actually process the request.

• These requests are called events and the code that processes the requests are called event handlers.

Command Pattern

Command Pattern

- ConcreteCommand: implementing the Execute method by invoking the corresponding operations on Receiver.

- Client: creates a ConcreteCommand object and sets its receiver.

- Invoker: asks the command to carry out the request.

- Receiver: knows how to perform the operations;

Command Pattern

CHÉÉÉÉÉM4

52

1

3

Command Pattern

Receiver

Invoker

Prisoner

Client

Command

CHÉÉÉÉÉM

Command Pattern

Command Pattern// Receiver.function Sword() { this.cut = function() { console.log("Sword: YAAAA! YOU'RE DEAD!"); } }// Command.function CutCommand(sword){ this.execute = function() { sword.cut(); }}// Invoker (may have a lot of commands)function Executioner() { this.cutHead = function(command) { command.execute(); }}

// Clientfunction King() { this.run = function(){ var sword = new Sword(); var command = new CutCommand (sword); var executioner = new Executioner();

executioner.cutHead(command); }}

(new King()).run();

DEMO

Command Pattern1. var Agent = {2. sayHello: function(name) { alert("Hello " + name); },3. sayGoodbye: function(name) { alert("Good bye " + name); },4. execute: function (name) {5. return Agent[name] && Agent[name].apply(Agent, [].slice.call(arguments, 1));6. }7. };

8. Agent.execute("sayHello", "Bill");

Command Pattern1. var Agent = {2. sayHello: function(name) { alert("Hello " +

name); },3. sayGoodbye: function(name) { alert("Good

bye " + name); },4. execute: function (name) {5. return Agent[name] &&

Agent[name].apply(Agent, [].slice.call(arguments, 1));

6. }7. };

8. Agent.execute("sayHello", "Bill");

It seems I have and will never use this pattern in Javascript.

Oh, really?

Command Patternvar button = document.getElementById('myBtn');

// add event listenerbutton.addEventListener('click', function(event) { closeWindow();});

...

What about this?

Command Patternvar button = document.getElementById('myBtn');

// add event listenerbutton.addEventListener('click', function(event) { closeWindow();});

...

Command Pattern

Command Pattern Event Driven

Command object

Receiver

Invoker

Source

Event object

Target object

Command Pattern

http://en.wikipedia.org/wiki/Command_pattern

Command Pattern Event Driven

Command object

Receiver

Invoker

Source

Event object

Target object

Command Pattern

Client Server

Add

Update

Delete

Normal way

Server

Command Pattern

Client

Add

Update

Delete

Invoker

Add

Update

Delete

Observer Pattern(Publish/Subscribe pattern)

Behavioral:

6

Observer Pattern

Hello, I’m Putin. I’d like to make an annual subscription.

register/subscribe

Subject

Aye aye sir.

Observer Pattern

ObserversONE TO MANY RELATIONSHIP

Subject

Observer Pattern

Observers

publish

Subject

Observer Pattern

Observers

I want to cancel my subscription ‘cause I’m tired to death.

remove/unsubscribe

Subject

Observer Pattern

Observers

Subject

Kicker

OK, take care, sir.

Observer Pattern

• Allows an object (subscriber or observer) to watch another object (publisher or subject).

• Subscribers are able to register/unregister (subscribe/unsubscribe) to receive topic notifications from the publisher.

• Publisher can notify observers by broadcasting (publishing) a notification to each observer.

Observer Pattern

Observer Pattern

Observer Pattern function Subject() { this.observers = [];} Subject.prototype = { subscribe: function(observer) { this.observers.push(observer); }, unsubscribe: function(name) { // remove observer from subscribers }, notify: function(message) { for (var i =0; i < this.observers.length; i++) { this.observers[i].callback(message); } }};

function Observer(name) { this.name = name; this.callback = function(message){ console.log(message) };}

// Here's where it gets used.var subject = new Subject();subject.subscribe(new Observer("Putin"));subject.subscribe(new Observer("Mary"));subject.subscribe(new Observer("Binladen"));subject.notify('Hello!');

Observer Pattern <input id='button1' type='button' value='Publish'><input class='subscriber' type='text'><input class='subscriber' type='text'>

$(function(){ function publish(){ $('.subscriber').trigger('/getnews', 'Hello!'); } $('.subscriber').on('/getnews', function(event, message){ this.value = message; }); $('#button1').click(publish);});

Javascript (jQuery):

More example: http://jsfiddle.net/rajeshpillai/xQkXk/22/

Observer Pattern

• The power of Loose Coupling– The only thing the subject knows about an observer

is that it implements a certain interface.– We can add new observers at any time.– We never need to modify the subject to add new

types of observers.– We can reuse subjects or observers independently of

each other.– Changes to either the subjects or an observer will no

affect the other.

Observer Pattern I found that the Observer pattern is so much like the Event-driven approach.

Are they the same?

Staff

Staff

Staff

Boss

notify

Hmm...

Observer Pattern

Staff

Staff

Staff

Event

Boss

Well, the main difference between them is the word “Event”.

Observer Pattern

Staff

Staff

Staff

Event

Boss

When Boss provides the instruction, the event will be responsible for transferring it to staff.

And Boss doesn’t care who the hell will receive his instruction.

Observer Pattern

As you well know, Leader works closely to the others...

In Observer, Leader knows all who will receive his instruction.

Member (Observer)

Member (Observer)

Member (Observer)

Leader (Subject)

Observer Pattern

So, Event-driven is more loose coupling than Observer pattern?

Yep! And actually, no one prevents you to implement Observer using Event-drivent.

Member (Observer)

Member (Observer)

Member (Observer)

Leader (Subject)

References

Finally

7

References

1. Learning JavaScript Design Patterns - Addy Osmani2. Head First Design Patterns - Eric Freeman & Elisabeth Freeman with

Kathy Sierra & Bert Bates3. OODesign - oodesign.com4. Javascript Design Pattern - ww.dofactory.com/javascript-pattern.aspx

The end!Thank you for your attention