# Design Patterns
Resources
- kamranahmedse/design-patterns-for-humans (opens new window)
- Learning JavaScript Design Patterns - addyosmani (opens new window)
- Toptal - The Comprehensive Guide to JavaScript Design Patterns (opens new window)
- wikipedia.org/wiki/Design_Patterns (opens new window)
- betterprogramming.pub/javascript-design-patterns-25f0faaaa15 (opens new window)
# Design Patterns
- Keep it Simple.
Design patterns in Javascript provide you with repeatable solutions to common problems. These are a few of the design patterns that are important to know.
- Creational - Creating objects
- Structural - Relation between objects
- Behavioral - Communication between objects
- Concurrency - Multi-threaded programming paradigms (Active object, Nuclear reaction, Scheduler)
Creational Patterns | Structural Patterns | Behavioral Patterns | Architectural |
---|---|---|---|
Simple Factory | Adapter | Chain of Responsibility | Module |
Factory Method | Bridge | Command | Constructor |
Abstract Factory | Composite | Iterator | MVC (Model-View-Controller) |
Builder | Decorator | Mediator | MVP (Model-View-Presenter) |
Prototype | Facade | Memento | MVVM (Model-View-ViewModel) |
Singleton | Flyweight | Observer | |
Proxy | Visitor | ||
Strategy | |||
State | |||
Template Method |
# Architectural Patterns
# Constructor
function Foo(params) {
this.params = params;
}
let obj = new Foo();
# Module
- Keep something as private yet accessible.
- Cons
- new property/methods added dynamically to obj has no access private variables..
let collection = (function() {
// private
let obj = [];
// public
return {
addObj: ....,
removeObj: ....,
getObj: ....
}
})();
collection.addObj({....});
# Creational Patterns
# Singleton
- Used when we need only one instance of a class.
- eg: configuration object, session object, etc
let singletonConfig = (function() {
let config;
function createConfig(options) {
// create a obj
}
return {
create: (options) => {
if (config === undefined) config = new createConfig(options);
else return "Already configured.";
},
get: () => {
if (config === undefined) return "Not configured.";
else return config;
},
};
})();
# Prototype
let proto = {
// some functions
};
function Foo(a) {
let a = a || "somethingdefault";
function constuctorFunc(x) {
this.x = x;
}
let instance = new constuctorFunc(a);
return instance;
}
// all instance share the proto functions instead of adding in all instances.
let obj = new Foo("data");
let obj2 = new Foo("data2");
# Structural Patterns
# Facade
- An abstraction layer. A simple interface use.
jQuery(".parent .child div.span");
# Behavioral Patterns
# Observer (Publisher/Subscriber)
- 2 parts - Subject and Observers.
- Publisher/Subscriber is a slight modified version of Observer Pattern. It is more loosely coupled.
- In Observer - subject has a references to subscribed Observers and calls methods directly
- In Publisher/Subscriber - A communication channel is used. Publisher just fires event and runs callback sent for that event.
- Useful in firing multiple callbacks for single event.
let publishSubscribe = {};
(function(container) {
let countId = 0;
// Subsribe
container.subscribe = (event, callback) => {
// register new event if not present
if (!(event in container)) container[event] = [];
// add callback to the list of callbacks
// All callbacks will be executed when event is fired
container[event].push({
"id: ++countId,
"callback": callback
});
};
// Unsubsribe
container.unsubscribe = (event, subscriberId) => {
// just remove subscriberId from container
};
// Publish
container.publish = (event, data) => [
for(subscriber of container[event]){
subscriber.callback(data);
}
];
})(publishSubscribe);
// Subscribe to events
let sub1 = publishSubscribe.subscribe('clickedFoo', foo());
let sub2 = publishSubscribe.subscribe('clickedFoo', foo2());
let sub3 = publishSubscribe.subscribe('clickedBar', bar());
// publish
publishSubscribe.publish('clickedFoo', {"data": "someEventData"}); // runs foo(), foo2()
publishSubscribe.publish('clickedBar', {"data": "someEventData"}); // runs bar()
# Command
- We create an abstraction layer(just like interface) over the actual executing code.
// Execution
let invoker = {
add: (x, y) => {
return x + y;
},
subtract: (x, y) => {
return x - y;
},
multiply: (x, y) => {
return x * y;
},
};
// abstraction (interface) layer
let abstractManager = {
execute: (name, ...args) => {
if (name in invoker) {
return invoker[name].apply(invoker, args);
}
},
};
manager.execute("add", 3, 5); // 8