├── .idea
├── encodings.xml
├── inspectionProfiles
│ └── Project_Default.xml
├── misc.xml
├── modules.xml
├── patterns.iml
├── scopes
│ └── scope_settings.xml
├── vcs.xml
└── watcherTasks.xml
├── README.md
├── behavioral
├── chain-of-resp.js
├── command.js
├── iterator.js
├── mediator.js
├── memento.js
├── observer.js
├── state.js
└── visitor.js
├── creational
├── abstract-factory.js
├── bulider.js
├── constructor.js
├── factory.js
├── prototype.js
└── singelton.js
├── index.html
└── structural
├── adapter.js
├── bridge.js
├── composite.js
├── decorator.js
├── facade.js
├── flyweight.js
└── proxy.js
/.idea/encodings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/.idea/inspectionProfiles/Project_Default.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/patterns.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/scopes/scope_settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/watcherTasks.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # JavaScript design patterns
2 |
3 | Here you will find most usefull (in my opinion) design patterns implemented in JavaScript. Enjoy! :)
4 |
5 | ## Creational
6 |
7 | * [Constructor](creational/constructor.js) functions that can be used to instantiate new objects
8 | * [Factory](creational/factory.js) factory simply generates an instance for client without exposing any instantiation logic to the client
9 | * [Prototype](creational/prototype.js) In this, we use a sort of a “skeleton” of an existing object to create or instantiate new objects.
10 | * [Singelton](creational/singelton.js) pattern where only one instance of a class can exist.
11 | * [Abstract Factory](creational/abstract-factory.js) factory that groups the individual but related/dependent factories together without specifying their concrete classes
12 | * [Builder](creational/bulider.js) separate the construction from its representation. Useful when there are a lot of steps involved in creation of an object.
13 |
14 |
15 | ## Structural
16 |
17 | * [Adapter](structural/adapter.js) lets you wrap an otherwise incompatible object in an adapter to make it compatible with another class.
18 | * [Bridge](structural/bridge.js) decouple an abstraction of object from its implementation so that the two can vary independently
19 | * [Composite](structural/composite.js) composes objects into tree-like structures to represent whole-part hierarchies
20 | * [Decorator](structural/decorator.js) lets you dynamically change the behavior of an object at run time by wrapping them in an object of a decorator class.
21 | * [Facade](structural/facade.js) provides a simplified interface to a complex subsystem. (jquery)
22 | * [Flyweight](structural/flyweight.js) minimize memory usage or computational expenses by sharing as much as possible with similar objects. (memoization, cache)
23 | * [Proxy](structural/proxy.js) provides a placeholder for another object to control access, reduce cost, and reduce complexity.
24 |
25 | ## Behavioral
26 |
27 | * [Chain Of Resp](behavioral/chain-of-resp.js) Request enters from one end and keeps going from object to object till it finds the suitable handler. (even propagation)
28 | * [Command](behavioral/command.js) provide the means to decouple client from receiver. (Reciver, Command, Invoker)
29 | * [Iterator](behavioral/iterator.js) way to sequentialy access the elements of an object without exposing the underlying presentation.
30 | * [Observer](behavioral/observer.js) defines a dependency between objects so that whenever an object changes its state, all its dependents are notified. (Pub/Sub, Observables)
31 | * [Visitor](behavioral/visitor.js) let's you add further operations to objects without having to modify them.
32 | * [Mediator](behavioral/mediator.js) control the interaction between two objects (chatroom, users)
33 | * [Memento](behavioral/memento.js) capturing and storing the current state of an object in a manner that it can be restored later on in a smooth manner. (edit ,save, restore)
34 | * [State](behavioral/state.js) lets you change the behavior of a class when the state changes. (waiting for payment ,procesing, shipping)
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/behavioral/chain-of-resp.js:
--------------------------------------------------------------------------------
1 | /*
2 | * The Chain of Responsibility pattern provides a chain of loosely coupled objects one of which can satisfy a request.
3 | * This pattern is essentially a linear search for an object that can handle a particular request.
4 | * An example of a chain-of-responsibility is event-bubbling in which an event propagates through a series of nested controls one of which may choose to handle the event.
5 | *
6 | * */
7 |
8 | //ES6
9 |
10 | class ShoppingCart {
11 | constructor() {
12 | this.products = [];
13 | }
14 |
15 | addProduct(p) {
16 | this.products.push(p);
17 | };
18 | }
19 |
20 | class Discount {
21 | calc(products) {
22 | let ndiscount = new NumberDiscount();
23 | let pdiscount = new PriceDiscount();
24 | let none = new NoneDiscount();
25 | ndiscount.setNext(pdiscount);
26 | pdiscount.setNext(none);
27 | return ndiscount.exec(products);
28 | };
29 | }
30 |
31 | class NumberDiscount {
32 | constructor() {
33 | this.next = null;
34 | }
35 |
36 | setNext(fn) {
37 | this.next = fn;
38 | };
39 |
40 | exec(products) {
41 | let result = 0;
42 | if (products.length > 3)
43 | result = 0.05;
44 |
45 | return result + this.next.exec(products);
46 | };
47 | }
48 |
49 | class PriceDiscount{
50 | constructor() {
51 | this.next = null;
52 | }
53 |
54 | setNext(fn) {
55 | this.next = fn;
56 | };
57 |
58 | exec(products) {
59 | let result = 0;
60 | let total = products.reduce((a, b)=> a + b);
61 |
62 | if (total >= 500)
63 | result = 0.1;
64 |
65 | return result + this.next.exec(products);
66 | };
67 | }
68 |
69 | class NoneDiscount {
70 | exec() {
71 | return 0;
72 | };
73 | }
74 |
75 |
76 | var cart = new ShoppingCart();
77 | cart.addProduct(30);
78 | cart.addProduct(130);
79 | cart.addProduct(230);
80 | cart.addProduct(230);
81 |
82 | //calculate discount depends on Price, Number of products etc..
83 | //discount request is bubling from one object to another
84 |
85 | var discount = new Discount().calc(cart.products);
86 |
87 | console.log(discount); //0.15000000000000002
88 |
89 |
90 |
91 | //ES 5
92 |
93 | function ShoppingCart() {
94 | this.products = [];
95 |
96 | this.addProduct = function(p) {
97 | this.products.push(p);
98 | };
99 | }
100 |
101 | function Discount() {
102 | this.calc = function(products) {
103 | var ndiscount = new NumberDiscount();
104 | var pdiscount = new PriceDiscount();
105 | var none = new NoneDiscount();
106 |
107 | ndiscount.setNext(pdiscount);
108 | pdiscount.setNext(none);
109 |
110 | return ndiscount.exec(products);
111 | };
112 | }
113 |
114 | function NumberDiscount() {
115 | this.next = null;
116 | this.setNext = function(fn) {
117 | this.next = fn;
118 | };
119 |
120 | this.exec = function(products) {
121 | var result = 0;
122 | if (products.length > 3)
123 | result = 0.05;
124 |
125 | return result + this.next.exec(products);
126 | };
127 | }
128 |
129 | function PriceDiscount() {
130 | this.next = null;
131 | this.setNext = function(fn) {
132 | this.next = fn;
133 | };
134 | this.exec = function(products) {
135 | var result = 0;
136 | var total = products.reduce(function(a, b) {
137 | return a + b;
138 | });
139 |
140 | if (total >= 500)
141 | result = 0.1;
142 |
143 | return result + this.next.exec(products);
144 | };
145 | }
146 |
147 | function NoneDiscount() {
148 | this.exec = function() {
149 | return 0;
150 | };
151 | }
152 |
153 | var cart = new ShoppingCart();
154 | cart.addProduct(30);
155 | cart.addProduct(130);
156 | cart.addProduct(230);
157 | cart.addProduct(230);
158 |
159 | //calculate discount depends on Price, Number of products etc..
160 | //discount request is bubling from one object to another
161 |
162 | var discount = new Discount().calc(cart.products);
163 |
164 | console.log(discount); //0.15000000000000002
--------------------------------------------------------------------------------
/behavioral/command.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Command Pattern
3 | * Allows you to encapsulate actions in objects.
4 | * The key idea behind this pattern is to provide the means to decouple client from receiver
5 | *
6 | * */
7 |
8 |
9 | // Receiver
10 | class Bulb {
11 | turnOn() {
12 | console.log('Bulb has been lit')
13 | }
14 |
15 | turnOff() {
16 | console.log('Darkness!')
17 | }
18 | }
19 |
20 | // Command
21 | class TurnOnCommand {
22 | constructor(bulb) {
23 | this.bulb = bulb
24 | }
25 |
26 | execute() {
27 | this.bulb.turnOn()
28 | }
29 |
30 | undo() {
31 | this.bulb.turnOff()
32 | }
33 |
34 | redo() {
35 | this.execute()
36 | }
37 | }
38 |
39 | class TurnOffCommand {
40 | constructor(bulb) {
41 | this.bulb = bulb
42 | }
43 |
44 | execute() {
45 | this.bulb.turnOff()
46 | }
47 |
48 | undo() {
49 | this.bulb.turnOn()
50 | }
51 |
52 | redo() {
53 | this.execute()
54 | }
55 | }
56 |
57 | // Invoker
58 | class RemoteControl {
59 | submit(command) {
60 | command.execute()
61 | }
62 | }
63 |
64 | const bulb = new Bulb();
65 |
66 | const turnOn = new TurnOnCommand(bulb);
67 | const turnOff = new TurnOffCommand(bulb);
68 |
69 | const remote = new RemoteControl();
70 | remote.submit(turnOn); // Bulb has been lit!
71 | remote.submit(turnOff); // Darkness!
--------------------------------------------------------------------------------
/behavioral/iterator.js:
--------------------------------------------------------------------------------
1 | /*
2 | *
3 | * It presents a way to access the elements of an object sequentially without exposing its underlying representation.
4 | *
5 | */
6 |
7 | class RadioStation {
8 | constructor(frequency) {
9 | this.frequency = frequency
10 | }
11 |
12 | getFrequency() {
13 | return this.frequency
14 | }
15 | }
16 |
17 | class StationList {
18 | constructor(){
19 | this.stations = []
20 | }
21 |
22 | addStation(station) {
23 | this.stations.push(station)
24 | }
25 |
26 | removeStation(toRemove) {
27 | const toRemoveFrequency = toRemove.getFrequency()
28 | this.stations = this.stations.filter(station => {
29 | return station.getFrequency() !== toRemoveFrequency
30 | })
31 | }
32 | }
33 |
34 | const stationList = new StationList()
35 |
36 | stationList.addStation(new RadioStation(89))
37 | stationList.addStation(new RadioStation(101))
38 | stationList.addStation(new RadioStation(102))
39 | stationList.addStation(new RadioStation(103.2))
40 |
41 | stationList.stations.forEach(station => console.log(station.getFrequency()))
42 |
43 | stationList.removeStation(new RadioStation(89)) // Will remove station 89
44 |
45 |
--------------------------------------------------------------------------------
/behavioral/mediator.js:
--------------------------------------------------------------------------------
1 | /*
2 | *
3 | * A Mediator is an object that coordinates interactions (logic and behavior) between multiple objects.
4 | * It makes decisions on when to call which objects, based on the actions (or inaction) of other objects and input.
5 | *
6 | * */
7 |
8 |
9 | // Mediator
10 | class ChatRoom {
11 | showMessage(user, message) {
12 | const time = new Date()
13 | const sender = user.getName()
14 |
15 | console.log(time + '[' + sender + ']:' + message)
16 | }
17 | }
18 |
19 | class User {
20 | constructor(name, chatMediator) {
21 | this.name = name
22 | this.chatMediator = chatMediator
23 | }
24 |
25 | getName() {
26 | return this.name
27 | }
28 |
29 | send(message) {
30 | this.chatMediator.showMessage(this, message)
31 | }
32 | }
33 |
34 | const mediator = new ChatRoom();
35 |
36 | const john = new User('John Doe', mediator);
37 | const jane = new User('Jane Doe', mediator);
38 |
39 | john.send('Hi there!')
40 | jane.send('Hey!')
41 |
42 | // Output will be
43 | // Feb 14, 10:58 [John]: Hi there!
44 | // Feb 14, 10:58 [Jane]: Hey!
--------------------------------------------------------------------------------
/behavioral/memento.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Memento pattern is about capturing and storing the current state of an object in a manner
3 | * that it can be restored later on in a smooth manner.
4 | * */
5 |
6 | class EditorMemento {
7 | constructor(content) {
8 | this._content = content
9 | }
10 |
11 | getContent() {
12 | return this._content
13 | }
14 | }
15 |
16 | class Editor {
17 | constructor(){
18 | this._content = ''
19 | }
20 |
21 | type(words) {
22 | this._content = this._content + ' ' + words
23 | }
24 |
25 | getContent() {
26 | return this._content
27 | }
28 |
29 | save() {
30 | return new EditorMemento(this._content)
31 | }
32 |
33 | restore(memento) {
34 | this._content = memento.getContent()
35 | }
36 | }
37 |
38 | const editor = new Editor();
39 |
40 | // Type some stuff
41 | editor.type('This is the first sentence.');
42 | editor.type('This is second.');
43 |
44 | // Save the state to restore to : This is the first sentence. This is second.
45 | const saved = editor.save();
46 |
47 | // Type some more
48 | editor.type('And this is third.');
49 |
50 | // Output: Content before Saving
51 | console.log(editor.getContent());// This is the first sentence. This is second. And this is third.
52 |
53 | // Restoring to last saved state
54 | editor.restore(saved);
55 |
56 | console.log(editor.getContent()); // This is the first sentence. This is second.
--------------------------------------------------------------------------------
/behavioral/observer.js:
--------------------------------------------------------------------------------
1 | /*
2 | *
3 | * It s a publish/subscribe pattern which allows a number of observer objects to see an event.
4 | * See - Observables / Rxjs
5 | *
6 | * */
7 |
8 | const JobPost = title => ({
9 | title: title
10 | })
11 |
12 | class JobSeeker {
13 | constructor(name) {
14 | this._name = name
15 | }
16 |
17 | notify(jobPost) {
18 | console.log(this._name, 'has been notified of a new posting :', jobPost.title)
19 | }
20 | }
21 |
22 | class JobBoard {
23 | constructor() {
24 | this._subscribers = []
25 | }
26 |
27 | subscribe(jobSeeker) {
28 | this._subscribers.push(jobSeeker)
29 | }
30 |
31 | addJob(jobPosting) {
32 | this._subscribers.forEach(subscriber => {
33 | subscriber.notify(jobPosting)
34 | })
35 | }
36 | }
37 |
38 | // Create subscribers
39 | const jonDoe = new JobSeeker('John Doe')
40 | const janeDoe = new JobSeeker('Jane Doe')
41 | const kaneDoe = new JobSeeker('Kane Doe')
42 |
43 | // Create publisher and attach subscribers
44 | const jobBoard = new JobBoard()
45 | jobBoard.subscribe(jonDoe)
46 | jobBoard.subscribe(janeDoe)
47 |
48 | // Add a new job and see if subscribers get notified
49 | jobBoard.addJob(JobPost('Software Engineer'))
50 |
51 | // Output
52 | // John Doe has been notified of a new posting : Software Engineer
53 | // Jane Doe has been notified of a new posting : Software Engineer
--------------------------------------------------------------------------------
/behavioral/state.js:
--------------------------------------------------------------------------------
1 | /*
2 | *
3 | * allows an object to alter its behavior when its internal state changes.
4 | *
5 | * */
6 |
7 | //ES6
8 | class OrderStatus {
9 | constructor(name, nextStatus) {
10 | this.name = name;
11 | this.nextStatus = nextStatus;
12 | }
13 |
14 | next() {
15 | return new this.nextStatus();
16 | }
17 | }
18 |
19 | class WaitingForPayment extends OrderStatus {
20 | constructor() {
21 | super('waitingForPayment', Shipping);
22 | }
23 | }
24 |
25 | class Shipping extends OrderStatus {
26 | constructor() {
27 | super('shipping', Delivered);
28 | }
29 | }
30 |
31 |
32 | class Delivered extends OrderStatus {
33 | constructor() {
34 | super('delivered', Delivered);
35 | }
36 | }
37 |
38 | class Order {
39 | constructor() {
40 | this.state = new WaitingForPayment();
41 | }
42 |
43 | nextState() {
44 | this.state = this.state.next();
45 | };
46 | }
47 |
48 | //ES5
49 | function Order() {
50 | this.state = new WaitingForPayment();
51 |
52 | this.nextState = function() {
53 | this.state = this.state.next();
54 | };
55 | }
56 |
57 |
58 | function WaitingForPayment() {
59 | this.name = 'waitingForPayment';
60 | this.next = function() {
61 | return new Shipping();
62 | };
63 | }
64 |
65 | function Shipping() {
66 | this.name = 'shipping';
67 | this.next = function() {
68 | return new Delivered();
69 | };
70 | }
71 |
72 | function Delivered() {
73 | this.name = 'delivered';
74 | this.next = function() {
75 | return this;
76 | };
77 | }
78 |
79 |
--------------------------------------------------------------------------------
/behavioral/visitor.js:
--------------------------------------------------------------------------------
1 | /*
2 | *
3 | * Visitor pattern let's you add further operations to objects without having to modify them.
4 | *
5 | * */
6 |
7 | class Monkey {
8 | shout() {
9 | console.log('Ooh oo aa aa!')
10 | }
11 |
12 | accept(operation) {
13 | operation.visitMonkey(this)
14 | }
15 | }
16 |
17 | class Lion {
18 | roar() {
19 | console.log('Roaaar!')
20 | }
21 |
22 | accept(operation) {
23 | operation.visitLion(this)
24 | }
25 | }
26 |
27 | class Dolphin {
28 | speak() {
29 | console.log('Tuut tuttu tuutt!')
30 | }
31 |
32 | accept(operation) {
33 | operation.visitDolphin(this)
34 | }
35 | }
36 |
37 | //Visitor
38 | const speak = {
39 | visitMonkey(monkey){
40 | monkey.shout()
41 | },
42 | visitLion(lion){
43 | lion.roar()
44 | },
45 | visitDolphin(dolphin){
46 | dolphin.speak()
47 | }
48 | }
49 |
50 | const monkey = new Monkey()
51 | const lion = new Lion()
52 | const dolphin = new Dolphin()
53 |
54 | monkey.accept(speak) // Ooh oo aa aa!
55 | lion.accept(speak) // Roaaar!
56 | dolphin.accept(speak) // Tuut tutt tuutt!
--------------------------------------------------------------------------------
/creational/abstract-factory.js:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | An Abstract Factory provide an interface for creating families of related or
4 | dependent objects without specifying their concrete classes.
5 |
6 | */
7 |
8 | /* ES5 */
9 | function droidProducer(kind) {
10 | if (kind === 'battle') return battleDroidFactory; //abstarct factory is not specifying anything - see factory method
11 | return pilotDroidFactory; //abstarct factory is not specifying anything - see factory method
12 | }
13 |
14 | function battleDroidFactory() {
15 | return new B1();
16 | }
17 |
18 | function pilotDroidFactory() {
19 | return new Rx24();
20 | }
21 |
22 | function B1() {}
23 | B1.prototype.info = function() {
24 | return "B1, Battle Droid";
25 | };
26 |
27 | function Rx24() {}
28 | Rx24.prototype.info = function() {
29 | return "Rx24, Pilot Droid";
30 | };
31 |
32 | var battleDroid = droidProducer('battle');
33 | var pilotDroid = droidProducer('pilot');
34 |
35 |
36 |
37 |
38 | /* ES6 */
39 | function droidProducer(kind) {
40 | if (kind === 'battle') return battleDroidFactory;
41 | return pilotDroidFactory;
42 | }
43 |
44 | function battleDroidFactory() {
45 | return new B1();
46 | }
47 |
48 | function pilotDroidFactory() {
49 | return new Rx24();
50 | }
51 |
52 |
53 | class B1 {
54 | info() {
55 | return "B1, Battle Droid";
56 | }
57 | }
58 |
59 | class Rx24 {
60 | info() {
61 | return "Rx24, Pilot Droid";
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/creational/bulider.js:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | Bulider pattern - are used to separate the complexities of the creation logic from the final representation.
4 |
5 | */
6 |
7 | /* ES 6 */
8 |
9 | class ProductBuilder {
10 | constructor() {
11 | this.name = 'A Product'
12 | this.price = 9.99
13 | this.category = 'other'
14 | }
15 |
16 | withName(name) {
17 | this.name = name
18 | return this
19 | }
20 |
21 | withPrice(price) {
22 | this.price = price
23 | return this
24 | }
25 |
26 | withCategory(category) {
27 | this.category = category
28 | return this
29 | }
30 |
31 | build() {
32 | return {
33 | name: this.name,
34 | price: this.price,
35 | category: this.category,
36 | }
37 | }
38 | }
39 |
40 | console.log(
41 | new ProductBuilder()
42 | .withName('Harry Potter')
43 | .withCategory('book')
44 | .build()
45 | );
46 |
47 | // =>
48 | // {
49 | // name: 'Harry Potter',
50 | // price: 9.99,
51 | // category: 'book'
52 | // }
53 |
54 |
55 | /* ES 5 */
56 |
57 | function ProductBuilder() {
58 |
59 | this.name = 'A Product';
60 | this.price = 9.99;
61 | this.category = 'other';
62 |
63 | this.withName = function(name){
64 | this.name = name;
65 | return this
66 | }
67 |
68 | this.withPrice = function(price) {
69 | this.price = price;
70 | return this
71 | }
72 |
73 | this.withCategory = function(category){
74 | this.category = category;
75 | return this
76 | }
77 |
78 | this.build = function() {
79 | return {
80 | name: this.name,
81 | price: this.price,
82 | category: this.category,
83 | }
84 | };
85 |
86 | }
87 |
88 |
89 | console.log(
90 | new ProductBuilder()
91 | .withName('Harry Potter')
92 | .withCategory('book')
93 | .build()
94 | );
95 |
96 | // =>
97 | // {
98 | // name: 'Harry Potter',
99 | // price: 9.99,
100 | // category: 'book'
101 | // }
--------------------------------------------------------------------------------
/creational/constructor.js:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | Constructor Pattern This is a class-based creational design pattern.
4 | Constructors are special functions that can be used to instantiate new objects with methods and properties defined by that function.
5 |
6 | */
7 |
8 |
9 | /* ES6 */
10 |
11 | class Hero {
12 | constructor(name, specialAbility) {
13 | // setting property values
14 | this._name = name;
15 | this._specialAbility = specialAbility;
16 |
17 | // declaring a method on the object
18 | this.getDetails = function() {
19 | return `${this._name} can ${this._specialAbility}`;
20 | };
21 | }
22 | }
23 |
24 | // creating new instances of Hero
25 | const IronMan = new Hero('Iron Man', 'fly');
26 |
27 | console.log(IronMan.getDetails()); // Iron Man can fly
28 |
29 |
30 | /* ES5 */
31 |
32 | function Hero(name, specialAbility) {
33 | // setting property values
34 | this.name = name;
35 | this.specialAbility = specialAbility;
36 |
37 | // declaring a method on the object
38 | this.getDetails = function() {
39 | return this.name + ' can ' + this.specialAbility;
40 | };
41 | }
42 |
--------------------------------------------------------------------------------
/creational/factory.js:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | Factory define an interface for creating a single object, but let subclasses decide which class to instantiate.
4 | Factory lets a class defer instantiation to subclasses.
5 |
6 | */
7 |
8 | /* ES6 */
9 |
10 | class BmwFactory {
11 |
12 | create(type) {
13 | if (type === 'X5')
14 | return new Bmw(type, 108000, 300); //factory method define what to run before return
15 | if (type === 'X6')
16 | return new Bmw(type, 111000, 320); //factory method define what to run before return
17 | }
18 | }
19 |
20 | class Bmw {
21 | constructor(model, price, maxSpeed) {
22 | this.model = model;
23 | this.price = price;
24 | this.maxSpeed = maxSpeed;
25 | }
26 | }
27 |
28 | const factory = new BmwFactory();
29 |
30 | const bmwX5 = factory.create('X5');
31 | const bmwX6 = factory.create('X6');
32 |
33 | console.log(bmwX6.model); //X6
34 | console.log(bmwX5.model); //X5
35 |
36 |
37 | /* ES5 */
38 |
39 | function bmwFactory(type) {
40 | if (type === 'X5')
41 | return new Bmw(type, 108000, 300);
42 | if (type === 'X6')
43 | return new Bmw(type, 111000, 320);
44 | }
45 |
46 | function Bmw(model, price, maxSpeed) {
47 | this.model = model;
48 | this.price = price;
49 | this.maxSpeed = maxSpeed;
50 | }
51 |
52 | var factory = new BmwFactory();
53 |
54 | var bmwX5 = factory.create('X5');
55 | var bmwX6 = factory.create('X6');
56 |
57 | console.log(bmwX6.model); //X6
58 | console.log(bmwX5.model); //X5
59 |
60 |
61 |
62 |
63 |
64 |
--------------------------------------------------------------------------------
/creational/prototype.js:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | Protoype Pattern is an object-based creational design pattern.
4 | In this, we use a sort of a “skeleton” of an existing object to create or instantiate new objects.
5 |
6 | This pattern is specifically important and beneficial to JavaScript because it utilizes prototypal inheritance
7 | instead of a classic object-oriented inheritance.
8 |
9 | */
10 |
11 |
12 | const car = {
13 | noOfWheels: 4,
14 | start() {
15 | return 'started';
16 | },
17 | stop() {
18 | return 'stopped';
19 | },
20 | };
21 |
22 | // Object.create(proto[, propertiesObject])
23 |
24 | const myCar = Object.create(car, { owner: { value: 'John' } });
25 |
26 | console.log(myCar.__proto__ === car); // true
--------------------------------------------------------------------------------
/creational/singelton.js:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | Singleton is a creational design pattern where only one instance of a class can exist.
4 | If no instance of the singleton class exists then a new instance is created and returned
5 | but if an instance already exists then the reference to the existing instance is returned.
6 |
7 | */
8 |
9 |
10 | // The following piece of code is a JavaScript developer's daily usage of the Singleton.
11 | var singelton = {
12 | publicVariable: 'Public Variable',
13 | publicMethod : function () {
14 | console.log('Public Method');
15 | }
16 | };
17 |
18 | /* ES5 */
19 |
20 | var Singleton = (function () {
21 |
22 | // Instance stores a reference to the Singleton
23 | var instance;
24 |
25 | function init() {
26 |
27 | // Private methods and variables
28 | return {
29 |
30 | // Public methods and variables
31 | publicMethod() {
32 | console.log('Public Method');
33 | },
34 |
35 | publicVariable: 'Public Variable'
36 | };
37 |
38 | };
39 |
40 | return {
41 |
42 | // Get the Singleton instance if one exists
43 | // or create one if it doesn't
44 | getInstance: function () {
45 |
46 | if ( !instance ) {
47 | instance = init();
48 | }
49 |
50 | return instance;
51 | }
52 |
53 | };
54 |
55 | })();
56 |
57 | var singleA = Singleton.getInstance();
58 | var singleB = Singleton.getInstance();
59 | console.log( singleA === singleB); // true
60 |
61 |
62 | /* ES6 */
63 |
64 | class Singleton {
65 |
66 | constructor() {
67 |
68 | this.publicVariable = 'Public Variable';
69 |
70 | if(! Singleton.instance){
71 | Singleton.instance = this;
72 | }
73 | return Singleton.instance;
74 | }
75 |
76 | publicMethod() {
77 | console.log('Public Method');
78 | }
79 | }
80 |
81 | const instance = new Singleton();
82 |
83 | // prevents new properties from being added to the object
84 | Object.freeze(instance);
85 |
86 |
87 | var singleA = new Singleton();
88 | var singleB = new Singleton();
89 | console.log( singleA === singleB); // true
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Title
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/structural/adapter.js:
--------------------------------------------------------------------------------
1 | /*
2 | Adapter Pattern
3 |
4 | This is a structural pattern where the interface of one class is translated into another.
5 | This pattern lets classes work together that could not otherwise because of incompatible interfaces.
6 |
7 | This pattern is often used to create wrappers for new refactored APIs so that other existing old APIs
8 | can still work with them.
9 |
10 | */
11 |
12 |
13 | // old interface
14 | class OldCalculator {
15 | constructor() {
16 | this.operations = function(term1, term2, operation) {
17 | switch (operation) {
18 | case 'add':
19 | return term1 + term2;
20 | case 'sub':
21 | return term1 - term2;
22 | default:
23 | return NaN;
24 | }
25 | };
26 | }
27 | }
28 |
29 | // new interface
30 | class NewCalculator {
31 | constructor() {
32 | this.add = function(term1, term2) {
33 | return term1 + term2;
34 | };
35 | this.sub = function(term1, term2) {
36 | return term1 - term2;
37 | };
38 | }
39 | }
40 |
41 | // Adapter Class
42 | class CalcAdapter {
43 | constructor() {
44 | const newCalc = new NewCalculator();
45 |
46 | this.operations = function(term1, term2, operation) {
47 | switch (operation) {
48 | case 'add':
49 | // using the new implementation under the hood
50 | return newCalc.add(term1, term2);
51 | case 'sub':
52 | return newCalc.sub(term1, term2);
53 | default:
54 | return NaN;
55 | }
56 | };
57 | }
58 | }
59 |
60 | // usage
61 | const oldCalc = new OldCalculator();
62 | console.log(oldCalc.operations(10, 5, 'add')); // 15
63 |
64 | const newCalc = new NewCalculator();
65 | console.log(newCalc.add(10, 5)); // 15
66 |
67 | const adaptedCalc = new CalcAdapter();
68 | console.log(adaptedCalc.operations(10, 5, 'add')); // 15;
--------------------------------------------------------------------------------
/structural/bridge.js:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | Bridge pattern - allows two components, a client and a service, to work together
4 | with each component having its own interface.
5 | Bridge is a high-level architectural pattern and its main goal is to write better code through two levels of abstraction.
6 |
7 | */
8 |
9 | class About{
10 | constructor(theme) {
11 | this.theme = theme
12 | }
13 |
14 | getContent() {
15 | return "About page in " + this.theme.getColor()
16 | }
17 | }
18 |
19 | class Careers{
20 | constructor(theme) {
21 | this.theme = theme
22 | }
23 |
24 | getContent() {
25 | return "Careers page in " + this.theme.getColor()
26 | }
27 | }
28 |
29 |
30 | class DarkTheme{
31 | getColor() {
32 | return 'Dark Black'
33 | }
34 | }
35 | class LightTheme {
36 | getColor() {
37 | return 'Off white'
38 | }
39 | }
40 |
41 |
42 | const darkTheme = new DarkTheme()
43 |
44 | const about = new About(darkTheme)
45 | const careers = new Careers(darkTheme)
46 |
47 | console.log(about.getContent() )// "About page in Dark Black"
48 | console.log(careers.getContent() )// "Careers page in Dark Black"
--------------------------------------------------------------------------------
/structural/composite.js:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | Composite pattern
4 | This is a structural design pattern that composes objects into tree-like structures to represent whole-part hierarchies.
5 | In this pattern, each node in the tree-like structure can be either an individual object
6 | or a composed collection of objects. Regardless, each node is treated uniformly.
7 |
8 | */
9 |
10 | /*ES6 */
11 |
12 | //Equipment
13 | class Equipment {
14 |
15 | getPrice() {
16 | return this.price || 0;
17 | }
18 |
19 | getName() {
20 | return this.name;
21 | }
22 |
23 | setName(name) {
24 | this.name = name;
25 | }
26 | }
27 |
28 | // --- composite ---
29 | class Composite extends Equipment {
30 |
31 | constructor() {
32 | super();
33 | this.equipments = [];
34 | }
35 |
36 | add(equipment) {
37 | this.equipments.push(equipment);
38 | }
39 |
40 | getPrice() {
41 | return this.equipments.map(equipment => {
42 | return equipment.getPrice();
43 | }).reduce((a, b) => {
44 | return a + b;
45 | });
46 | }
47 | }
48 |
49 | class Cabbinet extends Composite {
50 | constructor() {
51 | super();
52 | this.setName('cabbinet');
53 | }
54 | }
55 |
56 | // --- leafs ---
57 | class FloppyDisk extends Equipment {
58 | constructor() {
59 | super();
60 | this.setName('Floppy Disk');
61 | this.price = 70;
62 | }
63 | }
64 |
65 | class HardDrive extends Equipment {
66 | constructor() {
67 | super();
68 | this.setName('Hard Drive');
69 | this.price = 250;
70 | }
71 | }
72 |
73 | class Memory extends Equipment {
74 | constructor() {
75 | super();
76 | this.setName('Memory');
77 | this.price = 280;
78 | }
79 | }
80 |
81 | /*ES5 */
82 |
83 |
84 | // composition
85 | function EquipmentComposition(name) {
86 | this.equipments = [];
87 | this.name = name;
88 | }
89 |
90 | EquipmentComposition.prototype.add = function(equipment) {
91 | this.equipments.push(equipment);
92 | };
93 |
94 | EquipmentComposition.prototype.getPrice = function() {
95 | return this.equipments.map(function(equipment){
96 | return equipment.getPrice();
97 | }).reduce(function(a, b) {
98 | return a + b;
99 | });
100 | };
101 |
102 | function Equipment() {}
103 |
104 | Equipment.prototype.getPrice = function() {
105 | return this.price;
106 | };
107 |
108 | // -- leafs
109 | function FloppyDisk() {
110 | this.name = "Floppy Disk";
111 | this.price = 70;
112 | }
113 | FloppyDisk.prototype = Object.create(Equipment.prototype);
114 |
115 | function HardDrive() {
116 | this.name = "Hard Drive";
117 | this.price = 250;
118 | }
119 | HardDrive.prototype = Object.create(Equipment.prototype);
120 |
121 | function Memory() {
122 | this.name = "8gb memomry";
123 | this.price = 280;
124 | }
125 | Memory.prototype = Object.create(Equipment.prototype);
126 |
127 | module.exports = [EquipmentComposition, FloppyDisk, HardDrive, Memory];
--------------------------------------------------------------------------------
/structural/decorator.js:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | Decorator pattern - The decorator pattern (also known as Wrapper, an alternative naming shared with the Adapter pattern) is a design pattern that allows the behavior to be added to an individual object, e
4 | either statically or dynamically, without affecting the behavior of other objects from the same class.
5 |
6 |
7 | */
8 |
9 | //=======Single decorator
10 |
11 | function vehicle( vehicleType ){
12 |
13 | this.vehicleType = vehicleType || "car";
14 | this.model = "default";
15 | this.license = "00000-000";
16 |
17 | }
18 |
19 | var truck = new vehicle( "truck" );
20 |
21 | truck.setModel = function( modelName ){
22 | this.model = modelName;
23 | };
24 |
25 | truck.setColor = function( color ){
26 | this.color = color;
27 | };
28 |
29 | truck.setModel( "CAT" );
30 | truck.setColor( "blue" );
31 |
32 | console.log( truck ); // vehicle:truck, model:CAT, color: blue
33 |
34 | // "vehicle" is still unaltered
35 | var secondInstance = new vehicle( "car" );
36 | console.log( secondInstance );
37 |
38 |
39 |
40 |
41 | //=======Multiple decorators
42 |
43 |
44 | function MacBook() {
45 | this.cost = function () { return 997; };
46 | this.screenSize = function () { return 13.3; };
47 | }
48 |
49 | /*Decorator 1*/
50 | function Memory(macbook) {
51 | var v = macbook.cost();
52 | macbook.cost = function() {
53 | return v + 75;
54 | }
55 | }
56 | /*Decorator 2*/
57 | function Engraving( macbook ){
58 | var v = macbook.cost();
59 | macbook.cost = function(){
60 | return v + 200;
61 | };
62 | }
63 |
64 | /*Decorator 3*/
65 | function Insurance( macbook ){
66 | var v = macbook.cost();
67 | macbook.cost = function(){
68 | return v + 250;
69 | };
70 | }
71 | var mb = new MacBook();
72 | Memory(mb);
73 | Engraving(mb);
74 | Insurance(mb);
75 | console.log(mb.cost()); //1522
76 | console.log(mb.screenSize()); //13.3
--------------------------------------------------------------------------------
/structural/facade.js:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | Facade pattern - provides an interface that simplifies complex functionalities
4 |
5 | */
6 |
7 |
8 |
9 | /* Es 6 */
10 |
11 | class ShopFacade {
12 | constructor() {
13 | this.discount = new Discount();
14 | this.shipping = new Shipping();
15 | this.fees = new Fees();
16 | }
17 |
18 | calc(price) {
19 | price = this.discount.calc(price);
20 | price = this.fees.calc(price);
21 | price += this.shipping.calc();
22 | return price;
23 | }
24 | }
25 |
26 | class Discount {
27 | calc(value) {
28 | return value * 0.9;
29 | }
30 | }
31 |
32 | class Shipping {
33 | calc() {
34 | return 5;
35 | }
36 | }
37 |
38 | class Fees {
39 | calc(value) {
40 | return value * 1.05;
41 | }
42 | }
43 |
44 |
45 | /*ES 5 */
46 |
47 | var shopFacade = {
48 | calc: function(price) {
49 | price = discount(price);
50 | price = fees(price);
51 | price += shipping();
52 | return price;
53 | }
54 | };
55 |
56 | function discount(value) {
57 | return value * 0.9;
58 | }
59 |
60 | function shipping() {
61 | return 5;
62 | }
63 |
64 | function fees(value) {
65 | return value * 1.05;
66 | }
67 |
68 |
--------------------------------------------------------------------------------
/structural/flyweight.js:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | Flyweight pattern - The Flyweight pattern is a classical structural solution for optimizing code
4 | that is repetitive, slow, and inefficiently shares data. It aims to minimize the use of memory
5 | in an application. - see memoization & cacheing
6 |
7 | */
8 |
9 | /* Es6 */
10 | class Color {
11 | constructor(name){
12 | this.name = name
13 | }
14 | }
15 |
16 | class colorFactory {
17 | constructor(name){
18 | this.colors = {};
19 | }
20 | create(name) {
21 | let color = this.colors[name];
22 | if(color) return color;
23 | this.colors[name] = new Color(name);
24 | return this.colors[name];
25 | }
26 | };
27 |
28 | /* ES5 */
29 |
30 | function Color(name) {
31 | this.name = name;
32 | }
33 |
34 | var colorFactory = {
35 | colors: {},
36 | create: function(name) {
37 | var color = this.colors[name];
38 | if(color) return color;
39 |
40 | this.colors[name] = new Color(name);
41 | return this.colors[name];
42 | }
43 | };
--------------------------------------------------------------------------------
/structural/proxy.js:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | The Proxy pattern provides a surrogate or placeholder object for another object
4 | and controls access to this other object.
5 |
6 | */
7 |
8 | /* ES 6 */
9 |
10 | class BankAccounts{
11 |
12 | constructor(){ }
13 |
14 | add(bankAccountData) {
15 | console.log(bankAccountData);
16 | }
17 | find(bankAccount) {
18 | console.log(bankAccount);
19 | }
20 | getList() {
21 | console.log('list');
22 | }
23 | }
24 |
25 | class BankAccountsProxy{
26 |
27 | constructor(){
28 | this.bankAccounts = new BankAccounts();
29 | }
30 |
31 | addBankAccount(bankAccountData) {
32 | // some funtionality before calling the add method on BankAccounts
33 | console.log('proxied');
34 | return this.bankAccounts.add(bankAccountData);
35 | }
36 |
37 | findBankAccount(bankAccount) {
38 | // some funtionality before calling the find method on BankAccounts
39 | console.log('proxied');
40 | return this.bankAccounts.find(bankAccount);
41 | }
42 |
43 | getBankAccountsList() {
44 | // some funtionality before calling the getList method on BankAccounts
45 | console.log('proxied');
46 | return this.bankAccounts.getList();
47 | }
48 |
49 | }
50 |
51 | var proxy = new BankAccountsProxy();
52 | proxy.addBankAccount(123465787);
53 |
54 | /* ES 5 */
55 |
56 | function BankAccounts() {
57 | //constructor
58 | };
59 |
60 | BankAccounts.prototype = {
61 | add: function(bankAccountData) {
62 | console.log(bankAccountData);
63 | },
64 | find: function(bankAccount) {
65 | console.log(bankAccount);
66 | },
67 | getList: function() {
68 | console.log('list');
69 | }
70 | };
71 |
72 | // creating the proxy
73 | function BankAccountsProxy() {
74 | // getting a reference to the original object
75 | this.bankAccounts = new BankAccounts();
76 | };
77 |
78 | BankAccountsProxy.prototype = {
79 | addBankAccount: function(bankAccountData) {
80 | // some funtionality before calling the add method on BankAccounts
81 | console.log('proxied');
82 | return this.bankAccounts.add(bankAccountData);
83 | },
84 | findBankAccount: function(bankAccount) {
85 | // some funtionality before calling the find method on BankAccounts
86 | console.log('proxied');
87 | return this.bankAccounts.find(bankAccount);
88 | },
89 | getBankAccountsList: function() {
90 | // some funtionality before calling the getList method on BankAccounts
91 | console.log('proxied');
92 | return this.bankAccounts.getList();
93 | }
94 | };
95 |
96 | var proxy = new BankAccountsProxy();
97 | proxy.addBankAccount(123465787);
98 |
99 |
--------------------------------------------------------------------------------