├── .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 | 5 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 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 | 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 | --------------------------------------------------------------------------------