├── Chapter02 ├── B09381_02_01.js ├── B09381_02_02.js ├── B09381_02_03.js ├── B09381_02_04.js ├── B09381_02_05.js ├── B09381_02_06.js ├── B09381_02_07.js ├── B09381_02_08.js ├── B09381_02_09.js ├── B09381_02_10.js ├── B09381_02_11.js ├── B09381_02_12.js ├── B09831_02_13.js ├── B09831_02_14.js ├── B09831_02_15.js ├── B09831_02_16.js ├── B09831_02_17.js ├── B09831_02_18.js ├── B09831_02_19.js ├── B09831_02_20.js └── B09831_02_21.js ├── Chapter03 ├── B09381_03_01.js ├── B09381_03_010.js ├── B09381_03_011.js ├── B09381_03_012.js ├── B09381_03_013.0.js ├── B09381_03_013.1.js ├── B09381_03_014.js ├── B09381_03_015.js ├── B09381_03_016.js ├── B09381_03_017.js ├── B09381_03_018.0.js ├── B09381_03_018.1.js ├── B09381_03_019.js ├── B09381_03_02.html ├── B09381_03_020.js ├── B09381_03_021.js ├── B09381_03_022.js ├── B09381_03_023.js ├── B09381_03_024.js ├── B09381_03_025.js ├── B09381_03_026.js ├── B09381_03_027.js ├── B09381_03_028.js ├── B09381_03_029.js ├── B09381_03_03.js ├── B09381_03_030.js ├── B09381_03_031.js ├── B09381_03_032.js ├── B09381_03_033.js ├── B09381_03_034.js ├── B09381_03_035.js ├── B09381_03_036.js ├── B09381_03_037.js ├── B09381_03_038.js ├── B09381_03_039.js ├── B09381_03_04.js ├── B09381_03_040.js ├── B09381_03_041.js ├── B09381_03_042.js ├── B09381_03_043.js ├── B09381_03_044.js ├── B09381_03_045.js ├── B09381_03_046.js ├── B09381_03_05.js ├── B09381_03_06.js ├── B09381_03_07.js ├── B09381_03_08.js └── B09381_03_09.js ├── Chapter04 ├── B09381_04_01.js ├── B09381_04_02.js ├── B09381_04_03.js ├── B09381_04_04.html ├── B09381_04_05.html ├── B09381_04_06.js ├── B09381_04_07.js ├── B09381_04_08.js ├── B09381_04_09.js ├── B09381_04_10.js ├── B09381_04_11.js ├── B09381_04_12.js ├── B09381_04_13.js ├── B09381_04_14.js ├── B09381_04_15.js ├── B09381_04_16.js ├── B09381_04_17.js └── B09381_04_18.js ├── Chapter05 ├── .DS_Store ├── 001_Singleton │ ├── B09381_05_01.js │ └── B09381_05_02.js ├── 002_Prototype │ ├── B09381_05_03.01.js │ └── B09381_05_03.02.js ├── 003_Constructor │ └── B09381_05_04.js ├── 004_Factory_Method │ ├── Main.js │ ├── Pizza.js │ ├── PizzaStore.js │ ├── pizzas │ │ ├── CheesePizza.js │ │ └── ClamPizza.js │ └── stores │ │ └── NewYorkPizzaStore.js ├── 005_Abstract_Factory │ ├── Main.js │ ├── Pizza.js │ ├── PizzaIngredientFactory.js │ ├── PizzaStore.js │ ├── ingredientFactory │ │ └── NewYorkPizzaIngredientFactory.js │ ├── ingredients │ │ ├── FreshClam.js │ │ ├── Garlic.js │ │ ├── MarinaraSauce.js │ │ ├── Mushroom.js │ │ ├── RedPepper.js │ │ ├── ReggianoCheese.js │ │ └── ThinCrustDough.js │ ├── pizzas │ │ ├── CheesePizza.js │ │ └── ClamPizza.js │ └── stores │ │ └── NewYorkPizzaStore.js ├── 006_Builder │ ├── Item.js │ ├── Main.js │ ├── Meal.js │ ├── MealBuilder.js │ ├── Packing.js │ ├── items │ │ ├── Burger.js │ │ ├── Drink.js │ │ ├── SideDishes.js │ │ ├── burgers │ │ │ ├── BeefBurger.js │ │ │ ├── KobeBurger.js │ │ │ └── VeganBurger.js │ │ ├── drinks │ │ │ ├── Champagne.js │ │ │ ├── Coke.js │ │ │ └── Water.js │ │ └── side-dishes │ │ │ ├── Crudettes.js │ │ │ ├── Fries.js │ │ │ └── Salad.js │ └── packing │ │ ├── Bottle.js │ │ ├── BoxUp.js │ │ └── Wrapper.js └── B09381_05_0.js ├── Chapter06 ├── 001_Adapter │ ├── Duck.js │ ├── MallardDuck.js │ ├── Turkey.js │ ├── TurkeyAdapter.js │ ├── WildTurkey.js │ └── main.js ├── 002_Composite │ ├── CafeMenu.js │ ├── LunchMenu.js │ ├── Mattress.js │ ├── Menu.js │ ├── MenuComponent.js │ ├── MenuItem.js │ ├── PancakeHouseMenu.js │ └── main.js ├── 003_Decorator │ ├── Beverage.js │ ├── CondimentDecorator.js │ ├── Expresso.js │ ├── HouseBlend.js │ ├── Mocha.js │ ├── Whip.js │ └── main.js ├── 004_Bridge │ ├── Adventure4x4Car.js │ ├── Car.js │ ├── Color.js │ ├── FamilyCar.js │ ├── GreyColor.js │ ├── MatteBlackColor.js │ ├── RedColor.js │ ├── UrbanCar.js │ └── main.js ├── 005_Facade │ ├── Amplifier.js │ ├── BrPlayer.js │ ├── DummyConstructor.js │ ├── HomeTheaterFacade.js │ ├── Movie.js │ ├── Mp3Player.js │ ├── Nikita.js │ ├── Playable.js │ ├── PopcornPopper.js │ ├── Projector.js │ ├── Screen.js │ ├── Switchable.js │ ├── TheaterLights.js │ ├── Tuner.js │ └── main.js ├── 006_Flyweight │ ├── Forest.js │ ├── Tree.js │ ├── TreeFactory.js │ ├── TreeType.js │ └── main.js ├── 007_Proxy │ ├── PublicLibrary.js │ ├── PublicLibraryProxy.js │ └── main.js └── 008_VirtualProxy │ ├── PublicLibrary.js │ ├── PublicLibraryVirtualProxy.js │ ├── main.js │ └── utils.js ├── Chapter07 ├── Chain of responsibility │ ├── ATM.js │ ├── Currency.js │ ├── DispenseChain.js │ ├── FiftyBillDispenser.js │ ├── TenBillDispenser.js │ ├── TwentyBillDispenser.js │ └── main.js ├── Command │ ├── Command.js │ ├── Light.js │ ├── LightOnCommand.js │ ├── SimpleRemoteControl.js │ └── main.js ├── Composite Iterator │ ├── Mattress.js │ ├── Menu.js │ ├── MenuComponent.js │ ├── MenuItem.js │ └── main.js ├── Interpreter │ ├── Context.js │ ├── DecimalToRomanInterpreter.js │ ├── Interpreter.js │ └── main.js ├── Iterator │ ├── Iterator.js │ ├── LunchMenu.js │ ├── Mattress.js │ ├── Menu.js │ ├── MenuItem.js │ └── main.js ├── Mediator │ ├── Airplane.js │ ├── AirportControlTower.js │ ├── AirportLane.js │ ├── Mediator.js │ └── main.js ├── Memento │ ├── Caretaker.js │ ├── Command.js │ ├── Line.js │ ├── LineEditor.js │ ├── Memento.js │ ├── Originator.js │ ├── Painter.js │ ├── Snapshot.js │ └── main.js ├── Observer │ ├── CurrentConditionsDisplay.js │ ├── Displayable.js │ ├── Observable.js │ ├── Subject.js │ ├── WeatherStation.js │ └── main.js ├── State │ ├── Download.js │ ├── DownloadFailedState.js │ ├── DownloadPausedState.js │ ├── DownloadedState.js │ ├── DownloadingState.js │ ├── ReadyState.js │ ├── State.js │ └── main.js ├── Strategy │ ├── DecoyDuck.js │ ├── Duck.js │ ├── Flyable.js │ ├── MallardDuck.js │ ├── Quackable.js │ ├── RedheadDuck.js │ ├── RubberDuck.js │ └── main.js ├── Template Method │ ├── CaffeineBeverage.js │ ├── Coffee.js │ ├── Tea.js │ └── main.js └── Visitor │ ├── Book.js │ ├── PostageVisitor.js │ ├── ShoppingCart.js │ ├── Visitable.js │ ├── Visitor.js │ └── main.js ├── LICENSE └── README.md /Chapter02/B09381_02_01.js: -------------------------------------------------------------------------------- 1 | var pkt = pkt || {}; 2 | 3 | pkt.parser = { 4 | parseHTML: function (html) { 5 | // do something with html 6 | }, 7 | parseCSS: function (css) { 8 | // do something with css 9 | } 10 | }; 11 | -------------------------------------------------------------------------------- /Chapter02/B09381_02_02.js: -------------------------------------------------------------------------------- 1 | (function (namespace) { 2 | 'use strict'; 3 | function countAttributes(data) { 4 | // Do something generic with data 5 | } 6 | 7 | namespace.parser = { 8 | parseHTML: function (html) { 9 | var attributesLen = countAttributes(html); 10 | // do something with the HTML string 11 | }, 12 | parseCSS: function (css) { 13 | var attributesLen = countAttributes(css); 14 | // do something with the CSS string 15 | } 16 | }; 17 | 18 | }(this.pkt || (this.pkt = {}))); 19 | -------------------------------------------------------------------------------- /Chapter02/B09381_02_03.js: -------------------------------------------------------------------------------- 1 | (function (global, factory) { 2 | if (typeof define === "function" && define.amd) { 3 | define(["exports"], factory); 4 | } else if (typeof exports !== "undefined") { 5 | factory(exports); 6 | } else { 7 | var mod = { 8 | exports: {} 9 | }; 10 | factory(mod.exports); 11 | global.actual = mod.exports; 12 | } 13 | })(this, function (exports) { 14 | 'use strict'; 15 | 16 | Object.defineProperty(exports, "__esModule", { 17 | value: true 18 | }); 19 | 20 | exports.default = 22; 21 | }); 22 | 23 | -------------------------------------------------------------------------------- /Chapter02/B09381_02_04.js: -------------------------------------------------------------------------------- 1 | var GreetingModule = (function () { 2 | return { 3 | container: document.querySelector('#greet'), 4 | render: function () { 5 | this.container.innerHTML = 'Hi, there'; 6 | }, 7 | destroy: function () { 8 | this.container.innerHTML = ''; 9 | } 10 | }; 11 | }()); 12 | -------------------------------------------------------------------------------- /Chapter02/B09381_02_05.js: -------------------------------------------------------------------------------- 1 | (function (global) { 2 | var oContainer = null; 3 | function setContainer(oCont) { 4 | oContainer = oCont; 5 | } 6 | function addZero(nTime) { 7 | return nTime < 10? '0' + nTime : nTime; 8 | } 9 | function getFormattedTime(dTime) { 10 | return addZero(dTime.getHours()) + ':' + addZero(dTime.getMinutes()) + ':' + addZero(dTime.getSeconds()); 11 | } 12 | function render() { 13 | oContainer.innerHTML = 'Test Module: ' + getFormattedTime(new Date()); 14 | } 15 | function destroy() { 16 | oContainer.innerHTML = ''; 17 | } 18 | global.pkt.time = { 19 | render: function (oContainer) { 20 | setContainer(oContainer); 21 | render(); 22 | }, 23 | destroy: function () { 24 | destroy(); 25 | } 26 | }; 27 | }(this)); 28 | -------------------------------------------------------------------------------- /Chapter02/B09381_02_06.js: -------------------------------------------------------------------------------- 1 | define('utilities', [], function () { 2 | return { 3 | countAttributes: function (string) { 4 | // implementation code. 5 | } 6 | }; 7 | }); 8 | -------------------------------------------------------------------------------- /Chapter02/B09381_02_07.js: -------------------------------------------------------------------------------- 1 | define('parser', ['utilities'], function (utilities) { 2 | return { 3 | parseHTML: function (html) { 4 | var attributesLen = utilities.countAttributes(html); 5 | // do something with the HTML string 6 | }, 7 | parseCSS: function (css) { 8 | var attributesLen = utilities.countAttributes(css); 9 | // do something with the CSS string 10 | } 11 | } 12 | }); 13 | -------------------------------------------------------------------------------- /Chapter02/B09381_02_08.js: -------------------------------------------------------------------------------- 1 | var Utilities = { 2 | trim: function (string) { 3 | // implementation code should return a trimmed string. 4 | } 5 | }; 6 | module.exports = Utilities; 7 | -------------------------------------------------------------------------------- /Chapter02/B09381_02_09.js: -------------------------------------------------------------------------------- 1 | var Utilities = require('./utilities'); 2 | 3 | exports.parseHTML = function (html) { 4 | var trimmed = Utilities.trim(html); 5 | // do something with the trimmed HTML string 6 | }; 7 | exports.parseCSS = function (css) { 8 | var trimmed = utilities.trim(css); 9 | // do something with the trimmed CSS string 10 | }; 11 | -------------------------------------------------------------------------------- /Chapter02/B09381_02_10.js: -------------------------------------------------------------------------------- 1 | var parseHTML = require('parser').parseHTML; 2 | 3 | var html = 'Mastering Javascript Design Patterns 3rd editionThe best book I ever read'; 4 | 5 | console.log(parseHTML(html)); 6 | -------------------------------------------------------------------------------- /Chapter02/B09381_02_11.js: -------------------------------------------------------------------------------- 1 | var obj = {}; 2 | 3 | console.log(obj); 4 | 5 | // {} 6 | 7 | console.log(obj.constructor); 8 | 9 | // ƒ Object() { [native code] } 10 | 11 | console.log(obj.__proto__.constructor); 12 | 13 | // ƒ Object() { [native code] } 14 | 15 | console.log(obj.__proto__.__proto__.constructor); 16 | 17 | // Uncaught TypeError: Cannot read property 'constructor' of null 18 | -------------------------------------------------------------------------------- /Chapter02/B09381_02_12.js: -------------------------------------------------------------------------------- 1 | var arr = []; 2 | 3 | console.log(arr); 4 | // [] 5 | 6 | console.log(arr.constructor); 7 | // ƒ Array() { [native code] } 8 | 9 | console.log(arr.__proto__.constructor); 10 | // ƒ Array() { [native code] } 11 | 12 | console.log(arr.__proto__.__proto__.constructor); 13 | // ƒ Object() { [native code] } 14 | 15 | console.log(arr.__proto__.__proto__.__proto__.constructor); 16 | // Uncaught TypeError: Cannot read property 'constructor' of null 17 | -------------------------------------------------------------------------------- /Chapter02/B09831_02_13.js: -------------------------------------------------------------------------------- 1 | console.log(arr instanceof Array); 2 | // true 3 | console.log(arr instanceof Object); 4 | // true 5 | -------------------------------------------------------------------------------- /Chapter02/B09831_02_14.js: -------------------------------------------------------------------------------- 1 | function Mammal() {} 2 | Mammal.prototype.makeSound = function () { 3 | // do some sound 4 | }; 5 | 6 | function Dog() {} 7 | Dog.prototype = new Mammal(); 8 | Dog.prototype.constructor = Dog; 9 | var dog = new Dog(); 10 | 11 | console.log(dog); 12 | // Dog {} 13 | console.log(dog.__proto__.constructor); 14 | // ƒ Mammal() {} 15 | console.log(dog.__proto__.__proto__.constructor); 16 | // ƒ Mammal() {} 17 | console.log(dog.__proto__.__proto__.__proto__.constructor); 18 | //ƒ Object() { [native code] } 19 | -------------------------------------------------------------------------------- /Chapter02/B09831_02_15.js: -------------------------------------------------------------------------------- 1 | class Mammal { 2 | makeSound() { 3 | // do some sound 4 | } 5 | } 6 | 7 | class Dog extends Mammal{} 8 | 9 | var dog = new Dog(); 10 | 11 | console.log(dog); 12 | // Dog {} 13 | console.log(dog.__proto__.constructor); 14 | // class Dog extends Mammal{} 15 | console.log(dog.__proto__.__proto__.constructor); 16 | /* 17 | class Mammal { 18 | makeSound() { 19 | // do some sound 20 | } 21 | } 22 | */ 23 | console.log(dog.__proto__.__proto__.__proto__.constructor); 24 | //ƒ Object() { [native code] } 25 | -------------------------------------------------------------------------------- /Chapter02/B09831_02_16.js: -------------------------------------------------------------------------------- 1 | const _speed = new WeakMap(); 2 | const _speedStep = new WeakMap(); 3 | class Bicycle { 4 | constructor() { 5 | _speed.set(this, 0); 6 | _speedStep.set(this, 10); 7 | } 8 | getSpeed() { 9 | return _speed.get(this); 10 | } 11 | accelerate() { 12 | var speed = this.getSpeed(); 13 | var speedStep = _speedStep.get(this); 14 | _speed.set(this, speed + speedStep); 15 | } 16 | slowDown() { 17 | var speed = this.getSpeed(); 18 | var speedStep = _speedStep.get(this); 19 | _speed.set(this, speed - speedStep); 20 | } 21 | } 22 | 23 | var bicycle = new Bicycle(); 24 | 25 | console.log(bicycle.getSpeed()); 26 | // 0 27 | console.log(bicycle.accelerate()); 28 | // undefined 29 | console.log(bicycle.getSpeed()); 30 | // 10 31 | 32 | bicycle._speed = 100; 33 | 34 | console.log(bicycle.getSpeed()); 35 | // 10 36 | console.log(bicycle.accelerate()); 37 | // undefined 38 | console.log(bicycle.getSpeed()); 39 | // 20 40 | 41 | // Check that all the instances share the methods. 42 | var bicycle2 = new Bicycle(); 43 | 44 | console.log(bicycle.getSpeed === bicycle2.getSpeed); 45 | // true 46 | -------------------------------------------------------------------------------- /Chapter02/B09831_02_17.js: -------------------------------------------------------------------------------- 1 | class Bicycle { 2 | constructor() { 3 | this._speed = 0; 4 | this._speedStep = 10; 5 | } 6 | accelerate() { 7 | this._speed += this._speedStep; 8 | } 9 | slowDown() { 10 | this._speed -= this._speedStep; 11 | } 12 | } 13 | 14 | var bicycle = new Bicycle(); 15 | 16 | console.log(bicycle._speed); 17 | // 0 18 | console.log(bicycle._speedStep); 19 | // 10 20 | console.log(bicycle.accelerate()); 21 | // undefined 22 | console.log(bicycle._speed); 23 | // 10 24 | 25 | // But you can change the values from outside 26 | bicycle.speed = 100; 27 | 28 | console.log(bicycle.accelerate()); 29 | // undefined 30 | console.log(bicycle._speed); 31 | // 110 32 | -------------------------------------------------------------------------------- /Chapter02/B09831_02_18.js: -------------------------------------------------------------------------------- 1 | class Bicycle { 2 | constructor() { 3 | this._speed = 0; 4 | this._speedStep = 10; 5 | } 6 | accelerate() { 7 | this._speed += this._speedStep; 8 | } 9 | slowDown() { 10 | this._speed -= this._speedStep; 11 | } 12 | } 13 | 14 | var bicycle = new Bicycle(); 15 | 16 | console.log(bicycle._speed); 17 | // 0 18 | console.log(bicycle._speedStep); 19 | // 10 20 | console.log(bicycle.accelerate()); 21 | // undefined 22 | console.log(bicycle._speed); 23 | // 10 24 | 25 | // But you can change the values from outside 26 | bicycle.speed = 100; 27 | 28 | console.log(bicycle.accelerate()); 29 | // undefined 30 | console.log(bicycle._speed); 31 | // 110 32 | -------------------------------------------------------------------------------- /Chapter02/B09831_02_19.js: -------------------------------------------------------------------------------- 1 | // Regular expression to get all the attributes. 2 | const regex = /(\S+)=["']?((?:.(?!["']?\s+(?:\S+)=|[>"']))+.)["']?/g; 3 | 4 | // In the next line we expose countAttributes as the default value 5 | // to be used as a dependency. 6 | export default function countAttributes (str) { 7 | return regex.match(str).length; 8 | }; 9 | -------------------------------------------------------------------------------- /Chapter02/B09831_02_20.js: -------------------------------------------------------------------------------- 1 | import countAttributes from './utilities'; 2 | 3 | // This export method is used if you don't want to export anything by default 4 | // or if you want the user to pick up only what he needs. 5 | 6 | export const parseHTML = function (html) { 7 | var attributesLen = countAttributes(html); 8 | // do something with the HTML string 9 | }; 10 | 11 | export const parseCSS = function (css) { 12 | var attributesLen = countAttributes(css); 13 | // do something with the CSS string 14 | }; 15 | -------------------------------------------------------------------------------- /Chapter02/B09831_02_21.js: -------------------------------------------------------------------------------- 1 | import { parseHTML } from './parser'; 2 | 3 | const html = 'Mastering Javascript Design Patterns 3rd editionThe best book I ever read'; 4 | 5 | console.log(parseHTML(html)); 6 | -------------------------------------------------------------------------------- /Chapter03/B09381_03_01.js: -------------------------------------------------------------------------------- 1 | const image = new Image(); 2 | image.src = '../path/to/image/file.png'; 3 | 4 | document.body.appendChild(image); -------------------------------------------------------------------------------- /Chapter03/B09381_03_010.js: -------------------------------------------------------------------------------- 1 | export const getNextAccountId = (accounts) => { 2 | return accounts.length === 0 ? '0000000000000000' : accounts.slice(-1).accountId + 1; 3 | }; 4 | 5 | export const getIban = (data) => { 6 | return data.countryCode + data.checkDigits + 7 | data.bankIdentifier + data.branchIdentifier + data.accountId; 8 | } 9 | 10 | export const formatIban = (iban) => iban.match(/.{1,4}/g).join(' '); 11 | 12 | export const getNewAccount = (bankData, accounts) => { 13 | let accountId = getNextAccountId(accounts); 14 | 15 | let ibanFormatted = formatIban(getIban({ 16 | countryCode: bankData.countryCode, 17 | checkDigits: bankData.checkDigits, 18 | bankIdentifier: bankData.bankIdentifier, 19 | branchIdentifier: bankData.branchIdentifier, 20 | accountId 21 | })); 22 | 23 | return { 24 | accountId, 25 | ibanFormatted 26 | }; 27 | }; -------------------------------------------------------------------------------- /Chapter03/B09381_03_011.js: -------------------------------------------------------------------------------- 1 | import { getNewAccount } from './bank-utils'; 2 | import CyprusBank from './CyprusBank'; 3 | 4 | class CyprusBankBranch extends CyprusBank { 5 | constructor(branchIdentifier) { 6 | super(); 7 | this.branchIdentifier = branchIdentifier; 8 | this.accounts = []; 9 | } 10 | setNewAccount() { 11 | let newAccount = getNewAccount({ 12 | countryCode: this.countryCode, 13 | bankIdentifier: this.bankIdentifier, 14 | branchIdentifier: this.branchIdentifier, 15 | accountId 16 | }); 17 | 18 | this.accounts.push(newAccount); 19 | } 20 | } -------------------------------------------------------------------------------- /Chapter03/B09381_03_012.js: -------------------------------------------------------------------------------- 1 | document.getElementById('my-button').addEventListener('click', function (event) { 2 | // Do something when the button is clicked. 3 | }); -------------------------------------------------------------------------------- /Chapter03/B09381_03_013.0.js: -------------------------------------------------------------------------------- 1 | // ES5 example 2 | function Mammal() {} 3 | Mammal.prototype.makeSound = function () { 4 | // do some sound 5 | }; -------------------------------------------------------------------------------- /Chapter03/B09381_03_013.1.js: -------------------------------------------------------------------------------- 1 | // ES6 example 2 | class Mammal { 3 | makeSound() { 4 | // do some sound. 5 | } 6 | } -------------------------------------------------------------------------------- /Chapter03/B09381_03_014.js: -------------------------------------------------------------------------------- 1 | let xmlhttp = new XMLHttpRequest(); 2 | xmlhttp.onreadystatechange = () => { 3 | if (xmlhttp.readyState === 4 && xmlhttp.status === 200){ 4 | //process returned data 5 | } 6 | }; 7 | xmlhttp.open('GET', 'http://some.external.resource', true); 8 | xmlhttp.send(); -------------------------------------------------------------------------------- /Chapter03/B09381_03_015.js: -------------------------------------------------------------------------------- 1 | let xmlhttp; 2 | const requestData = () => { 3 | xmlhttp = new XMLHttpRequest() 4 | xmlhttp.onreadystatechange = processData; 5 | xmlhttp.open('GET', 'http://some.external.resource', true); 6 | xmlhttp.send(); 7 | } 8 | const processData = () => { 9 | if (xmlhttp.readyState === 4 && xmlhttp.status === 200){ 10 | //process returned data 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Chapter03/B09381_03_016.js: -------------------------------------------------------------------------------- 1 | $.getJSON('http://some.external.resource', (json) => { 2 | //process returned data 3 | }); -------------------------------------------------------------------------------- /Chapter03/B09381_03_017.js: -------------------------------------------------------------------------------- 1 | $.ajax('http://some.external.resource', { 2 | success: (json) => { 3 | //process returned data 4 | }, 5 | error: () =>{ 6 | //process failure 7 | }, 8 | dataType: 'json' 9 | }); -------------------------------------------------------------------------------- /Chapter03/B09381_03_018.0.js: -------------------------------------------------------------------------------- 1 | var greet = function (greeting) { 2 | return function (name) { 3 | return greeting + ' ' + name + '!'; 4 | }; 5 | }; 6 | 7 | var sayHelloTo = greet('Hello'); 8 | sayHelloTo('Douglas'); // Hello Douglas! -------------------------------------------------------------------------------- /Chapter03/B09381_03_018.1.js: -------------------------------------------------------------------------------- 1 | const greet = (greeting) => (name) => `${greeting} ${name}!`; 2 | 3 | var sayHelloTo = greet('Hello'); 4 | sayHelloTo('Douglas'); // Hello Douglas! -------------------------------------------------------------------------------- /Chapter03/B09381_03_019.js: -------------------------------------------------------------------------------- 1 | let x = [a,b,c,d]; 2 | let y = x.map(ƒ); 3 | 4 | /* ƒ receives three parameters 5 | * 1. item 6 | * 2. index of the item in the array. 7 | * 3. the array that map is iterating. 8 | * 9 | * ƒ returns a new item that will be added to the new array. 10 | */ 11 | 12 | x === y; // false -------------------------------------------------------------------------------- /Chapter03/B09381_03_02.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /Chapter03/B09381_03_020.js: -------------------------------------------------------------------------------- 1 | let numbers = [1, 2, 3, 4]; 2 | 3 | const duplicateValuesInArray = (numbersArray) => { 4 | for(let i = 0, len = numbersArray.length; i < len; i++) { 5 | numbersArray[i] = numbersArray[i] * 2; 6 | } 7 | } 8 | 9 | duplicateValuesInArray(numbers); // [2, 4, 6, 8] -------------------------------------------------------------------------------- /Chapter03/B09381_03_021.js: -------------------------------------------------------------------------------- 1 | const duplicate = (number) => number * 2; 2 | 3 | const duplicateValuesInArray = (numbersArray) => { 4 | return numbersArray.map(duplicate); 5 | }; 6 | 7 | let numbers = [1, 2, 3, 4]; 8 | duplicateValuesInArray(numbers); -------------------------------------------------------------------------------- /Chapter03/B09381_03_022.js: -------------------------------------------------------------------------------- 1 | let x = [a,b,c,d]; 2 | let y = x.reduce(ƒ, 0); // 3 | 4 | /* ƒ receives 4 parameters 5 | * 1. accumulator 6 | * 2. current value 7 | * 3. index 8 | * 4. the array that reduce is iterating. 9 | * 10 | * ƒ returns the accumulated data 11 | */ 12 | 13 | x === y; // false -------------------------------------------------------------------------------- /Chapter03/B09381_03_023.js: -------------------------------------------------------------------------------- 1 | let numbers = [1, 2, 4, 6, 8, 9]; 2 | 3 | function sum(numbers) { 4 | var total = 0; 5 | for(let i = 0; i < numbers.length; i++) { 6 | total += numbers[i]; 7 | } 8 | return numbers; 9 | } 10 | 11 | sum(numbers); // 30; -------------------------------------------------------------------------------- /Chapter03/B09381_03_024.js: -------------------------------------------------------------------------------- 1 | let numbers = [1, 2, 4, 6, 8, 9]; 2 | 3 | const add = (op1,op2) => op1 + op2; 4 | const sum = (numbers) => numbers.reduce(add, 0); 5 | 6 | sum(numbers); // 30 -------------------------------------------------------------------------------- /Chapter03/B09381_03_025.js: -------------------------------------------------------------------------------- 1 | let tags = ['packt', 'structural', 'packt', 'behavioral', 'es6', 'patterns', 'es6', 'es6']; 2 | let reducedTags = {}; 3 | const reducer = (accumulator,tag) => { 4 | var tagInAccumulatorValue = accumulator[tag]; 5 | accumulator[tag] = tagInAccumulatorValue ? tagInAccumulatorValue + 1 : 1; 6 | return accumulator; 7 | }; 8 | 9 | const tagProcessor = (tags) => tags.reduce(reducer, reducedTags); 10 | 11 | tagProcessor(tags); // {packt: 2, structural: 1, behavioral: 1, es6: 3, patterns: 1} -------------------------------------------------------------------------------- /Chapter03/B09381_03_026.js: -------------------------------------------------------------------------------- 1 | let x = [a,b,c,d]; 2 | let y = x.filter(ƒ); // 3 | 4 | /* ƒ receives 3 parameters 5 | * 1. item 6 | * 3. index 7 | * 4. the array that map is iterating. 8 | * 9 | * ƒ returns true or false. 10 | */ 11 | 12 | x === y; // false -------------------------------------------------------------------------------- /Chapter03/B09381_03_027.js: -------------------------------------------------------------------------------- 1 | const greetings = ['hello', 'dog', 'goodbye']; 2 | 3 | const filterGreetings = (greetings) => { 4 | const filteredGreetings = []; 5 | for(let i = 0; i < greetings.length; i++) { 6 | let greeting = greetings[i]; 7 | if(greeting !== 'dog') { 8 | filteredGreetings.push(greeting); 9 | } 10 | } 11 | return filteredGreetings; 12 | } 13 | 14 | filterGreetings(greetings); // ['hello', 'goodbye']; -------------------------------------------------------------------------------- /Chapter03/B09381_03_028.js: -------------------------------------------------------------------------------- 1 | const greetings = ['hello', 'dog', 'goodbye']; 2 | 3 | const removeDog = (item) => { 4 | return item !== 'dog'; 5 | }; 6 | const filterGreetings = (greetings) => greetings.filter(removeDog); 7 | 8 | filterGreetings(greetings); // ['hello', 'goodbye'] -------------------------------------------------------------------------------- /Chapter03/B09381_03_029.js: -------------------------------------------------------------------------------- 1 | export const bills = [ 2 | { 3 | date: 'Sat Mar 31 2018 09:08:00 GMT+0200 (CEST)', 4 | waiter: 'Lorenzo', 5 | items: [ 6 | { 7 | description: 'ristretto', 8 | cost: 2.8 9 | } 10 | ] 11 | }, 12 | /* Other data objects */ 13 | ]; -------------------------------------------------------------------------------- /Chapter03/B09381_03_03.js: -------------------------------------------------------------------------------- 1 | const _drawer = new WeakMap(); 2 | export default class CashRegister { 3 | constructor() { 4 | _drawer.set(this, [500, 200, 100, 50, 20, 10, 5, 2, 1]); 5 | } 6 | get drawer() { 7 | return _drawer.get(this); 8 | } 9 | makeChange(bill, tendered) { 10 | let difference = tendered - bill; 11 | let i = 0; 12 | const change = []; 13 | let denomination = this.drawer[0]; 14 | 15 | while(difference > 0) { 16 | if(difference < denomination) { 17 | i++; 18 | denomination = this.drawer[i]; 19 | continue; 20 | } 21 | change.push(denomination); 22 | difference -= denomination; 23 | } 24 | return change; 25 | } 26 | } -------------------------------------------------------------------------------- /Chapter03/B09381_03_030.js: -------------------------------------------------------------------------------- 1 | /* 2 | * filterByTwoMatches 3 | * Gets a bill as a parameter and returns true if it has two product matches. 4 | * @param {Object} bill 5 | * @returns boolean 6 | */ 7 | export const filterByTwoMatches = (bill) => bill.items.productMatches === 2; -------------------------------------------------------------------------------- /Chapter03/B09381_03_031.js: -------------------------------------------------------------------------------- 1 | export const flattenItems = { 2 | /** 3 | * flattenItems.reducer 4 | * Gets two parameters the accumulator and the item and returns an accumulator. 5 | * On each iteration it adds the cost of the product and if the description matches 6 | * 'cappuccino' or 'muffin' it increments productMatches. 7 | * @param {Object} accumulator 8 | * @param {Object} item 9 | * @returns Object 10 | */ 11 | reducer(accumulator, item) { 12 | if(['cappuccino', 'muffin'].includes(item.description)) { 13 | const productMatches = accumulator.productMatches; 14 | accumulator.productMatches = productMatches > 0 ? productMatches + 1 : 1; 15 | } 16 | accumulator.cost += item.cost; 17 | return accumulator; 18 | }, 19 | // Initial accumulator value 20 | initialValue: { productMatches: 0, cost: 0 } 21 | }; 22 | 23 | export const finalReport = { 24 | /** 25 | * finalReport.reducer 26 | * Gets three parameters, the accumulator, the bill and the index 27 | * If it's the first time we find a waiter we add it to the waiters 28 | * object and we set the value to one, in any other case we just add one 29 | * Increases the amount of bills and we increase the total amount with 30 | * the value of the cost of the bill. 31 | * At the end it gets the average of the ticket. 32 | * @param {Object} accumulator 33 | * @param {Object} bill 34 | * @param {Number} index 35 | * @returns Object 36 | */ 37 | reducer(accumulator, bill, index) { 38 | const waiter = accumulator.waiters[bill.waiter]; 39 | accumulator.waiters[bill.waiter] = waiter > 0 ? waiter + 1 : 1; 40 | accumulator.billsAmount++; 41 | accumulator.totalAmount += bill.items.cost; 42 | accumulator.averageTicket = accumulator.totalAmount / (index + 1); 43 | return accumulator; 44 | }, 45 | // Initial accumulator value 46 | initialValue: { waiters: {}, totalAmount: 0, averageTicket: 0, billsAmount: 0 } 47 | }; -------------------------------------------------------------------------------- /Chapter03/B09381_03_032.js: -------------------------------------------------------------------------------- 1 | import { flattenItems } from './reducers'; 2 | 3 | /** 4 | * flattenBill 5 | * Gets a bill as a parameter and returns a new object with only 6 | * the needed for the purpose. 7 | * The object contains two properties: 8 | * - waiter => Name of the waiter 9 | * - items => A new object with the cost of the whole bill and how many product matches the expected types 10 | * @param {Object} bill 11 | * @returns Object 12 | */ 13 | export const flattenBill = (bill) => { 14 | return { 15 | waiter: bill.waiter, 16 | items: bill.items.reduce(flattenItems.reducer, flattenItems.initialValue) 17 | }; 18 | }; -------------------------------------------------------------------------------- /Chapter03/B09381_03_033.js: -------------------------------------------------------------------------------- 1 | import bills from './bills'; 2 | import { flattenBill } from './mappers'; 3 | import { filterByTwoMatches } from './filters'; 4 | import { finalReport } from './reducers'; 5 | 6 | /** 7 | * getCappuccinoMuffinData 8 | * Gets an array of bills and returns an object with all the expected data. 9 | * 1. It maps the object to flatten the items object in something more manageable. 10 | * 2. It filters the array to get a subset of elements that have consumed a muffin 11 | * and a cappuccino. 12 | * 3. It reduces all the information to a single object with all the data. 13 | * @param {Array} bills 14 | * @returns Object 15 | */ 16 | const getCappuccinoMuffinData = (bills) => { 17 | return bills 18 | .map(flattenBill) 19 | .filter(filterByTwoMatches) 20 | .reduce(finalReport.reducer, finalReport.initialValue); 21 | }; 22 | 23 | console.log(getCappuccinoMuffinData(bills)); 24 | // { waiters: { Sophia :1, Lorenzo :2}, totalAmount :16.5, averageTicket :5.5, billsAmount :3 } -------------------------------------------------------------------------------- /Chapter03/B09381_03_034.js: -------------------------------------------------------------------------------- 1 | const { Map } = require('immutable'); 2 | const map = Map({ a: { fullname: 'John Doe' }, b: { fullname: 'Jane Doe' } }); 3 | const set1 = map.set('a', { fullname: 'James Sullivan' }); 4 | const set2 = map.set('a', { fullname: 'Joe McCloud' }); 5 | 6 | set1.get('a') !== set2.get('a') && set1.get('b') === set2.get('b'); // true -------------------------------------------------------------------------------- /Chapter03/B09381_03_035.js: -------------------------------------------------------------------------------- 1 | const factorial = (num) => { 2 | if(num < 0) { 3 | return -1; 4 | } else if (num == 0) { 5 | return 1; 6 | } else { 7 | return (num * factorial(num - 1)); 8 | } 9 | }; 10 | 11 | factorial(10); // 3628800 12 | factorial(11); // 39916800 13 | factorial(1000000) // Maximum call stack size exceeded -------------------------------------------------------------------------------- /Chapter03/B09381_03_036.js: -------------------------------------------------------------------------------- 1 | const factorial = (number, accumulator = 1) => { 2 | if(number < 2) { 3 | return accumulator; 4 | } 5 | return factorial(number - 1, number * accumulator); 6 | } -------------------------------------------------------------------------------- /Chapter03/B09381_03_037.js: -------------------------------------------------------------------------------- 1 | const privateFactorial = {}; 2 | const factorial = (num) => { 3 | if(privateFactorial[num]) { 4 | return privateFactorial[num]; 5 | } 6 | if(num < 0) { 7 | return -1; 8 | } else if (num == 0) { 9 | return 1; 10 | } else { 11 | return (privateFactorial[num] = num * factorial(num - 1)); 12 | } 13 | }; 14 | 15 | console.time(); // API to track the time a function takes to execute. 16 | factorial(150); // 8.159152832478977e+47 17 | console.timeEnd(); // Returns the ms taken to end the execution. 18 | // default: 0.072998046875ms 19 | 20 | console.time(); // API to track the time a function takes to execute. 21 | factorial(150); // 8.159152832478977e+47 22 | console.timeEnd(); // Returns the ms taken to end the execution. 23 | // default: 0.0048828125ms -------------------------------------------------------------------------------- /Chapter03/B09381_03_038.js: -------------------------------------------------------------------------------- 1 | const memoize = (fn) => { 2 | let cache = {}; 3 | return (...args) => { // Use rest arguments to get an array 4 | let n = args[0]; // Get the first argument. 5 | if (n in cache) { 6 | return cache[n]; // Returns the cached value 7 | } else { 8 | let result = fn(n); // Executes the function 9 | cache[n] = result; // Caches the result. 10 | return result; 11 | } 12 | } 13 | } -------------------------------------------------------------------------------- /Chapter03/B09381_03_039.js: -------------------------------------------------------------------------------- 1 | const factorial = (number, accumulator = 1) => { 2 | if(number < 2) { 3 | return accumulator; 4 | } 5 | return factorial(number - 1, number * accumulator); 6 | }; 7 | 8 | const factorialMemo = memoize(factorial); 9 | 10 | console.time(); 11 | factorialMemo(150); // 5.7133839564458575e+262 12 | console.timeEnd(); // default: 0.094970703125ms 13 | 14 | console.time(); 15 | factorialMemo(150); // 5.7133839564458575e+262 16 | console.timeEnd(); // default: 0.008056640625ms -------------------------------------------------------------------------------- /Chapter03/B09381_03_04.js: -------------------------------------------------------------------------------- 1 | import CashRegister from './CashRegister'; 2 | const cashRegister = new CashRegister(); 3 | cashRegister.makeChange(400, 555); // [100, 50, 5] -------------------------------------------------------------------------------- /Chapter03/B09381_03_040.js: -------------------------------------------------------------------------------- 1 | const Bread = ((() => { 2 | return class Bread { 3 | constructor(breadType) { 4 | this.breadType = breadType; 5 | //some complex, time consuming operation 6 | console.log("Bread " + breadType + " created.") 7 | } 8 | }; 9 | })()); -------------------------------------------------------------------------------- /Chapter03/B09381_03_041.js: -------------------------------------------------------------------------------- 1 | const Bakery = ((() => { 2 | return class Bakery { 3 | constructor() { 4 | this.requiredBreads = []; 5 | } 6 | orderBreadType(breadType) { 7 | this.requiredBreads.push(breadType); 8 | } 9 | }; 10 | })()); -------------------------------------------------------------------------------- /Chapter03/B09381_03_042.js: -------------------------------------------------------------------------------- 1 | const Bakery = ((() => { 2 | return class Bakery { 3 | constructor() { 4 | this.requiredBreads = []; 5 | } 6 | orderBreadType(breadType) { 7 | this.requiredBreads.push(breadType); 8 | } 9 | pickUpBread(breadType) { 10 | console.log("Pickup of bread " + breadType + " requested"); 11 | if (!this.breads) { 12 | this.createBreads(); 13 | } 14 | return this.breads.filter((bread) => { 15 | return bread.breadType === breadType; 16 | })[0]; 17 | } 18 | createBreads() { 19 | this.breads = this.requiredBreads.map((requiredBread) => { 20 | return new Bread(requiredBread); 21 | }); 22 | } 23 | }; 24 | })()); -------------------------------------------------------------------------------- /Chapter03/B09381_03_043.js: -------------------------------------------------------------------------------- 1 | var bakery = new Westeros.FoodSuppliers.Bakery(); 2 | bakery.orderBreadType("Brioche"); 3 | bakery.orderBreadType("Anadama bread"); 4 | bakery.orderBreadType("Chapati"); 5 | bakery.orderBreadType("Focaccia"); 6 | 7 | console.log(bakery.pickUpBread("Brioche").breadType + "picked up"); -------------------------------------------------------------------------------- /Chapter03/B09381_03_044.js: -------------------------------------------------------------------------------- 1 | const createEmailCreator = (domain) => { 2 | return (username) => `${username}@${domain}`; 3 | }; 4 | 5 | const createGmailAccounts = createEmailCreator('gmail.com'); 6 | const createHotmailAccounts = createEmailCreator('hotmail.com'); 7 | 8 | createGmailAccounts('john_doe'); // john_doe@gmail.com 9 | createGmailAccounts('jane_doe'); // jane_doe@gmail.com 10 | 11 | createHotmailAccounts('jimmy_santillana'); // jimmy_santillana@hotmail.com 12 | createHotmailAccounts('gerard_picard'); // gerard_piccard@hotmail.com -------------------------------------------------------------------------------- /Chapter03/B09381_03_045.js: -------------------------------------------------------------------------------- 1 | const hyphenate = (word) => word.match(new RegExp(`.{1,${Math.floor(word.length/2)}}`, 'g')).join('-'); 2 | 3 | const reverse = (word) => word.split('').reverse().join(''); 4 | 5 | const toUpperCase = (word) => word.toUpperCase(); 6 | 7 | const words = ['web', 'design', 'patterns']; 8 | 9 | let newWords = words.map(hyphenate); 10 | newWords = newWords.map(reverse); 11 | newWords = newWords.map(toUpperCase); 12 | 13 | 14 | newWords; // ["B-E-W", "NGI-SED", "SNRE-TTAP"] -------------------------------------------------------------------------------- /Chapter03/B09381_03_046.js: -------------------------------------------------------------------------------- 1 | const compose = (...fns) => fns.reverse().reduce((prevFn, nextFn) => 2 | value => nextFn(prevFn(value)), 3 | value => value 4 | ); 5 | const hyphenate = (word) => word.match(new RegExp(`.{1,${Math.ceil(word.length/2)}}`, 'g')).join('-'); 6 | 7 | const reverse = (word) => word.split('').reverse().join(''); 8 | 9 | const toUpperCase = (word) => word.toUpperCase(); 10 | 11 | const composed = compose(hyphenate, reverse, toUpperCase); 12 | 13 | const words = ['web', 'design', 'patterns']; 14 | 15 | words.map(composed); // ["BE-W", "NGI-SED", "SNRE-TTAP"] -------------------------------------------------------------------------------- /Chapter03/B09381_03_05.js: -------------------------------------------------------------------------------- 1 | import getChange from './change'; 2 | 3 | const _drawer = new WeakMap(); 4 | class CashRegister { 5 | constructor() { 6 | _drawer.set(this, [500, 200, 100, 50, 20, 10, 5, 2, 1]); 7 | } 8 | get drawer() { 9 | return _drawer.get(this); 10 | } 11 | makeChange(bill, tendered) { 12 | return getChange(tendered - bill, this.drawer); 13 | } 14 | } -------------------------------------------------------------------------------- /Chapter03/B09381_03_06.js: -------------------------------------------------------------------------------- 1 | const getChange = (difference, denominations) => { 2 | if(difference === 0) { 3 | return []; 4 | } 5 | if(denominations.length === 0) { 6 | return false; 7 | } 8 | if(difference < denominations[0]) { 9 | return getChange(difference, denominations.slice(1)); 10 | } else { 11 | return [denominations[0]].concat(getChange(difference - denominations[0], denominations)); 12 | } 13 | }; -------------------------------------------------------------------------------- /Chapter03/B09381_03_07.js: -------------------------------------------------------------------------------- 1 | import CashRegister from './CashRegister'; 2 | const cashRegister = new CashRegister(); 3 | cashRegister.makeChange(400, 555); // [100, 50, 5] -------------------------------------------------------------------------------- /Chapter03/B09381_03_08.js: -------------------------------------------------------------------------------- 1 | import CyprusBankBranch from './CyprusBankBranch'; 2 | 3 | const cyprusBankBranch0128 = new CyprusBankBranch('0128'); 4 | 5 | cyprusBankBranch0128.accounts; // [] 6 | 7 | cyprusBankBranch0128.generateAccount(); // undefined[{…}] 8 | 9 | cyprusBankBranch0128.accounts 10 | /** 11 | * [{ 12 | * accountId: "0000000000000000", 13 | * ibanFormatted: "CY95 0020 0128 0000 0000 0000 0000" 14 | * .}] 15 | */ -------------------------------------------------------------------------------- /Chapter03/B09381_03_09.js: -------------------------------------------------------------------------------- 1 | import CyprusBank from './CyprusBank'; 2 | 3 | class CyprusBankBranch extends CyprusBank { 4 | constructor(branchIdentifier) { 5 | super(); 6 | this.branchIdentifier = branchIdentifier; 7 | this.accounts = []; 8 | } 9 | generateAccount() { 10 | let accountId; 11 | this.accounts.length > 0 ? 12 | accountId = this.accounts[this.accounts.length - 1].accountId + 1 : 13 | accountId = '0000000000000000'; 14 | let ibanFormatted = (this.countryCode + Math.floor(Math.random() * 100) + 15 | this.bankIdentifier + this.branchIdentifier + 16 | accountId).match(/.{1,4}/g).join(' '); 17 | this.accounts.push({ accountId, ibanFormatted }); 18 | } 19 | } -------------------------------------------------------------------------------- /Chapter04/B09381_04_01.js: -------------------------------------------------------------------------------- 1 | var item = document.getElementById("item1"); 2 | item. addEventListener("click", function(event){ /*do something */ }); -------------------------------------------------------------------------------- /Chapter04/B09381_04_02.js: -------------------------------------------------------------------------------- 1 | [1, 4, 6, 9, 34, 56, 77, 1, 2, 3, 6, 10] -------------------------------------------------------------------------------- /Chapter04/B09381_04_03.js: -------------------------------------------------------------------------------- 1 | [1, 4, 6, 9, 34, 56, 77, 1, 2, 3, 6, 10].filter((x)=>x%2==0) => 2 | [4, 6, 34, 56, 2, 6, 10] -------------------------------------------------------------------------------- /Chapter04/B09381_04_04.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /Chapter04/B09381_04_05.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Chapter04/B09381_04_06.js: -------------------------------------------------------------------------------- 1 | var incrementSubscription = source.subscribe(() => counter++); 2 | var subscription = source.filter(x=>counter%2==0).subscribe(function (e) { 3 | output.innerHTML = "Clicked " + counter + " time" +(counter > 1 ? "s" : ""); 4 | }); -------------------------------------------------------------------------------- /Chapter04/B09381_04_07.js: -------------------------------------------------------------------------------- 1 | source.buffer(() => source.debounce(250)) 2 | .map((list) => list.length) 3 | .filter((x) => x >= 2) 4 | .subscribe((x)=> { 5 | counter++; 6 | output.innerHTML = "Clicked " + counter + " time" + (counter > 1 ? "s" : ""); 7 | }); -------------------------------------------------------------------------------- /Chapter04/B09381_04_08.js: -------------------------------------------------------------------------------- 1 | Rx.Observable 2 | .FromEvent(button, "click") 3 | .debounce(1000).subscribe((x)=>doSomething()); -------------------------------------------------------------------------------- /Chapter04/B09381_04_09.js: -------------------------------------------------------------------------------- 1 | var deaths = [ 2 | { 3 | Name:"Stannis", 4 | Cause: "Cold" 5 | }, 6 | { 7 | Name: "Tyrion", 8 | Cause: "Stabbing" 9 | }, 10 | … 11 | ]; -------------------------------------------------------------------------------- /Chapter04/B09381_04_10.js: -------------------------------------------------------------------------------- 1 | function generateDeathsStream(deaths) { 2 | return Rx.Observable.from(deaths).zip(Rx.Observable.interval(500), (death,_)=>death); 3 | } -------------------------------------------------------------------------------- /Chapter04/B09381_04_11.js: -------------------------------------------------------------------------------- 1 | generateDeathsStream(deaths).sample(1500).subscribe((item) => { /*do something */ }); -------------------------------------------------------------------------------- /Chapter04/B09381_04_12.js: -------------------------------------------------------------------------------- 1 | var counter = 0; 2 | generateDeathsStream(deaths).subscribe((item) => { counter++ }); -------------------------------------------------------------------------------- /Chapter04/B09381_04_13.js: -------------------------------------------------------------------------------- 1 | var button1 = document.getElementById("button1"); 2 | var button2 = document.getElementById("button2"); 3 | var button3 = document.getElementById("button3"); 4 | var button1Stream = Rx.Observable.fromEvent(button1, 'click'); 5 | var button2Stream = Rx.Observable.fromEvent(button2, 'click'); 6 | var button3Stream = Rx.Observable.fromEvent(button3, 'click'); 7 | var messageStream = Rx.Observable.merge(button1Stream, button2Stream, button3Stream); 8 | messageStream.subscribe(function (x) { return console.log(x.type + " on " + x.srcElement.id); }); -------------------------------------------------------------------------------- /Chapter04/B09381_04_14.js: -------------------------------------------------------------------------------- 1 | var worker = Rx.DOM.fromWorker("worker.js"); 2 | button4Stream.subscribe(function (_) { 3 | worker.onNext({ cmd: "start", number: 35 }); 4 | }); -------------------------------------------------------------------------------- /Chapter04/B09381_04_15.js: -------------------------------------------------------------------------------- 1 | var messageStream = Rx.Observable.merge(button1Stream, button2Stream, button3Stream, worker); 2 | messageStream.subscribe( 3 | function (x) { 4 | appendToOutput(x.type + (x.srcElement.id === undefined ? " with " + x.data : " on " + x.srcElement.id)); 5 | }, 6 | function (err) { return appendToOutput(err, true); } 7 | ); -------------------------------------------------------------------------------- /Chapter04/B09381_04_16.js: -------------------------------------------------------------------------------- 1 | var messageStream = Rx.Observable.merge(button1Stream, button2Stream, button3Stream, worker); 2 | var intervalStream = Rx.Observable.interval(5000); 3 | messageStream 4 | .zip(intervalStream, function (x, _) {return x;}) 5 | .subscribe(function (x) { 6 | toastr.info(x.type + (x.srcElement.id === undefined ? " with " + x.data : " on " + x.srcElement.id)); 7 | }, 8 | function (err) { return toastr.error(err); } 9 | ); -------------------------------------------------------------------------------- /Chapter04/B09381_04_17.js: -------------------------------------------------------------------------------- 1 | var processors = Rx.Observable.amb(processorStream1, processorStream2); -------------------------------------------------------------------------------- /Chapter04/B09381_04_18.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Mastering-JavaScript-Design-Patterns-Third-Edition/a43051f47d39ae122009f70c611c13fb83907daa/Chapter04/B09381_04_18.js -------------------------------------------------------------------------------- /Chapter05/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Mastering-JavaScript-Design-Patterns-Third-Edition/a43051f47d39ae122009f70c611c13fb83907daa/Chapter05/.DS_Store -------------------------------------------------------------------------------- /Chapter05/001_Singleton/B09381_05_01.js: -------------------------------------------------------------------------------- 1 | const obj = {}; -------------------------------------------------------------------------------- /Chapter05/001_Singleton/B09381_05_02.js: -------------------------------------------------------------------------------- 1 | let DBCInstance = null; 2 | global.DatabaseConnection = class DatabaseConnection { 3 | get url() { 4 | return 'mongodb://localhost:27017/myproject'; 5 | } 6 | get username() { 7 | return 'admin'; 8 | } 9 | get connection() { 10 | let connection; 11 | // Do something to get the connection to the DB. 12 | return connection; 13 | } 14 | get password() { 15 | return 'localhost'; 16 | } 17 | static get instance() { 18 | if(DBCInstance === null || 19 | DBCInstance.getConnection().isClosed()) { 20 | DBCInstance = new DatabaseConnection(); 21 | } 22 | return DBCInstance; 23 | } 24 | } 25 | 26 | DatabaseConnection.instance; -------------------------------------------------------------------------------- /Chapter05/002_Prototype/B09381_05_03.01.js: -------------------------------------------------------------------------------- 1 | class HumanBeing { 2 | constructor(config) { 3 | this.skinColor = config.skinColor; 4 | this.hairColor = config.hairColor; 5 | this.height = config.height; 6 | this.weight = config.weight; 7 | this.gender = config.gender; 8 | // And more data. 9 | } 10 | } 11 | 12 | var me = new HumanBeing({ skinColor: 'pale', hairColor: 'brown', height:'173cm', weight: '100kg', gender: 'male'}); 13 | 14 | var clone = Object.assign(new HumanBeing({}), me); 15 | 16 | me === clone; // false but the data they contain is the same. -------------------------------------------------------------------------------- /Chapter05/002_Prototype/B09381_05_03.02.js: -------------------------------------------------------------------------------- 1 | class HumanBeing { 2 | constructor(config) { 3 | this.skinColor = config.skinColor; 4 | this.hairColor = config.hairColor; 5 | this.height = config.height; 6 | this.weight = config.weight; 7 | this.gender = config.gender; 8 | // And more data. 9 | } 10 | clone() { 11 | return new HumanBeing(Object.assign({}, this)); 12 | } 13 | } 14 | 15 | var me = new HumanBeing({ skinColor: 'pale', hairColor: 'brown', height:'173cm', weight: '100kg', gender: 'male'}); 16 | 17 | var clone = me.clone(); 18 | 19 | me === clone; // false but the data they contain is the same. -------------------------------------------------------------------------------- /Chapter05/003_Constructor/B09381_05_04.js: -------------------------------------------------------------------------------- 1 | class Car { 2 | constructor(color, brand, doors, miles) { 3 | this.color = color; 4 | this.brand = brand; 5 | this.doors = doors; 6 | this.miles = miles; 7 | } 8 | } 9 | 10 | var myCar = new Car('blue', 'Mercedes', 2, 9000); -------------------------------------------------------------------------------- /Chapter05/004_Factory_Method/Main.js: -------------------------------------------------------------------------------- 1 | import NewYorkPizzaStore from './stores/NewYorkPizzaStore'; 2 | 3 | var oPizzaStore = new NewYorkPizzaStore(); 4 | oPizzaStore.orderPizza("cheese"); -------------------------------------------------------------------------------- /Chapter05/004_Factory_Method/Pizza.js: -------------------------------------------------------------------------------- 1 | class Pizza { 2 | constructor({ name = '', dough = '', sauce = '', toppings = []}) { 3 | this.name = name; 4 | this.dough = dough; 5 | this.sauce = sauce; 6 | this.toppings = toppings; 7 | } 8 | 9 | prepare() { 10 | console.log("Preparing " + this.name); 11 | console.log("Tossing dough..."); 12 | console.log("Adding sauce"); 13 | console.log("Adding toppings:"); 14 | 15 | for (let topping of this.toppings) { 16 | console.log(topping + " "); 17 | } 18 | } 19 | 20 | bake() { 21 | console.log("Bake for 25 minutes at 350"); 22 | } 23 | 24 | cut() { 25 | console.log("Cutting the pizza into diagonal slices"); 26 | } 27 | 28 | box() { 29 | console.log("Place pizza in official PizzaStore box"); 30 | } 31 | 32 | getName() { 33 | return this.name; 34 | } 35 | } 36 | 37 | export default Pizza; -------------------------------------------------------------------------------- /Chapter05/004_Factory_Method/PizzaStore.js: -------------------------------------------------------------------------------- 1 | class PizzaStore { 2 | createPizza() { 3 | throw new Error("This method must be overwritten!"); 4 | } 5 | 6 | orderPizza(type) { 7 | let pizza = this.createPizza(type); 8 | 9 | pizza.prepare(); 10 | pizza.bake(); 11 | pizza.cut(); 12 | pizza.box(); 13 | } 14 | } 15 | 16 | export default PizzaStore; -------------------------------------------------------------------------------- /Chapter05/004_Factory_Method/pizzas/CheesePizza.js: -------------------------------------------------------------------------------- 1 | import Pizza from "../Pizza"; 2 | 3 | class CheesePizza extends Pizza { 4 | constructor() { 5 | super({ 6 | name: "NY Style Sauce and Cheese Pizza", 7 | dough: "Thin Crust Dough", 8 | sauce: "Marinara sauce", 9 | toppings: ["Grated Reggiano Cheese"] 10 | }); 11 | } 12 | } 13 | 14 | export default CheesePizza; 15 | -------------------------------------------------------------------------------- /Chapter05/004_Factory_Method/pizzas/ClamPizza.js: -------------------------------------------------------------------------------- 1 | import Pizza from '../Pizza'; 2 | 3 | class ClamPizza extends Pizza { 4 | constructor() { 5 | super({ 6 | name: 'NY Style Sauce and Clam Pizza', 7 | dough: 'Thin Crust Dough', 8 | sauce: 'Marinara sauce', 9 | toppings: ["Grated Reggiano Cheese", 'Clams'] 10 | }); 11 | } 12 | } 13 | 14 | export default ClamPizza; -------------------------------------------------------------------------------- /Chapter05/004_Factory_Method/stores/NewYorkPizzaStore.js: -------------------------------------------------------------------------------- 1 | import PizzaStore from '../PizzaStore'; 2 | import CheesePizza from '../pizzas/CheesePizza'; 3 | import ClamPizza from '../pizzas/ClamPizza'; 4 | 5 | const PIZZAS = { 6 | cheese: CheesePizza, 7 | clam: ClamPizza 8 | }; 9 | 10 | class NewYorkPizzaStore extends PizzaStore { 11 | createPizza(type) { 12 | let PizzaConstructor = PIZZAS[type]; 13 | let pizza = null; 14 | if (PizzaConstructor) { 15 | pizza = new PizzaConstructor(); 16 | } 17 | return pizza; 18 | } 19 | } 20 | 21 | export default NewYorkPizzaStore; -------------------------------------------------------------------------------- /Chapter05/005_Abstract_Factory/Main.js: -------------------------------------------------------------------------------- 1 | import NewYorkPizzaStore from './stores/NewYorkPizzaStore'; 2 | 3 | var oPizzaStore = new NewYorkPizzaStore(); 4 | oPizzaStore.orderPizza("cheese"); -------------------------------------------------------------------------------- /Chapter05/005_Abstract_Factory/Pizza.js: -------------------------------------------------------------------------------- 1 | class Pizza { 2 | constructor({ name = '', dough = null, sauce = null, veggies = [], cheese = null, pepperoni = null, clams = null }) { 3 | this.name = name; 4 | this.dough = dough; 5 | this.sauce = sauce; 6 | this.veggies = veggies; 7 | this.cheese = cheese; 8 | this.pepperoni = pepperoni; 9 | this.clams = clams; 10 | } 11 | 12 | prepare() { 13 | throw new Error("This method must be overwritten!"); 14 | } 15 | 16 | bake() { 17 | console.log("Bake for 25 minutes at 350"); 18 | } 19 | 20 | cut() { 21 | console.log("Cutting the pizza into diagonal slices"); 22 | } 23 | 24 | box() { 25 | console.log("Place pizza in official PizzaStore box"); 26 | } 27 | 28 | getName() { 29 | return this.name; 30 | } 31 | 32 | setName(name) { 33 | this.name = name; 34 | } 35 | } 36 | 37 | export default Pizza; -------------------------------------------------------------------------------- /Chapter05/005_Abstract_Factory/PizzaIngredientFactory.js: -------------------------------------------------------------------------------- 1 | class PizzaIngredientFactory { 2 | createDough() { 3 | throw new Error("This method must be overwritten!"); 4 | } 5 | 6 | createSauce() { 7 | throw new Error("This method must be overwritten!"); 8 | } 9 | 10 | createCheese() { 11 | throw new Error("This method must be overwritten!"); 12 | } 13 | 14 | createVeggies() { 15 | throw new Error("This method must be overwritten!"); 16 | } 17 | 18 | createPepperoni() { 19 | throw new Error("This method must be overwritten!"); 20 | } 21 | 22 | createClam() { 23 | throw new Error("This method must be overwritten!"); 24 | } 25 | } 26 | 27 | export default PizzaIngredientFactory; -------------------------------------------------------------------------------- /Chapter05/005_Abstract_Factory/PizzaStore.js: -------------------------------------------------------------------------------- 1 | class PizzaStore { 2 | createPizza() { 3 | throw new Error("This method must be overwritten!"); 4 | } 5 | 6 | orderPizza(type) { 7 | let pizza = this.createPizza(type); 8 | 9 | pizza.prepare(); 10 | pizza.bake(); 11 | pizza.cut(); 12 | pizza.box(); 13 | } 14 | } 15 | 16 | export default PizzaStore; -------------------------------------------------------------------------------- /Chapter05/005_Abstract_Factory/ingredientFactory/NewYorkPizzaIngredientFactory.js: -------------------------------------------------------------------------------- 1 | import PizzaIngredientFactory from '../PizzaIngredientFactory'; 2 | import ThinCrustDough from '../ingredients/ThinCrustDough'; 3 | import MarinaraSauce from '../ingredients/MarinaraSauce'; 4 | import ReggianoCheese from '../ingredients/ReggianoCheese'; 5 | import Garlic from '../ingredients/Garlic'; 6 | import Mushroom from '../ingredients/Mushroom'; 7 | import Pepperoni from '../ingredients/Pepperoni'; 8 | import RedPepper from '../ingredients/RedPepper'; 9 | import FreshClam from '../ingredients/FreshClam'; 10 | 11 | class NewYorkPizzaIngredientFactory extends PizzaIngredientFactory { 12 | createDough() { 13 | return new ThinCrustDough(); 14 | } 15 | 16 | createSauce() { 17 | return new MarinaraSauce(); 18 | } 19 | 20 | createCheese() { 21 | return new ReggianoCheese(); 22 | } 23 | 24 | createVeggies() { 25 | return [new Garlic(), new Mushroom(), new RedPepper()]; 26 | } 27 | 28 | createClam() { 29 | return new FreshClam(); 30 | } 31 | } 32 | 33 | export default NewYorkPizzaIngredientFactory; -------------------------------------------------------------------------------- /Chapter05/005_Abstract_Factory/ingredients/FreshClam.js: -------------------------------------------------------------------------------- 1 | class FreshClam {} 2 | 3 | export default FreshClam; -------------------------------------------------------------------------------- /Chapter05/005_Abstract_Factory/ingredients/Garlic.js: -------------------------------------------------------------------------------- 1 | class Garlic {} 2 | 3 | export default Garlic; -------------------------------------------------------------------------------- /Chapter05/005_Abstract_Factory/ingredients/MarinaraSauce.js: -------------------------------------------------------------------------------- 1 | class MarinaraSauce {} 2 | 3 | export default MarinaraSauce; -------------------------------------------------------------------------------- /Chapter05/005_Abstract_Factory/ingredients/Mushroom.js: -------------------------------------------------------------------------------- 1 | class Mushroom {} 2 | 3 | export default Mushroom; -------------------------------------------------------------------------------- /Chapter05/005_Abstract_Factory/ingredients/RedPepper.js: -------------------------------------------------------------------------------- 1 | class RedPepper {} 2 | 3 | export default RedPepper; -------------------------------------------------------------------------------- /Chapter05/005_Abstract_Factory/ingredients/ReggianoCheese.js: -------------------------------------------------------------------------------- 1 | class ReggianoCheese {} 2 | 3 | export default ReggianoCheese; -------------------------------------------------------------------------------- /Chapter05/005_Abstract_Factory/ingredients/ThinCrustDough.js: -------------------------------------------------------------------------------- 1 | class ThinCrustDough {} 2 | 3 | export default ThinCrustDough; -------------------------------------------------------------------------------- /Chapter05/005_Abstract_Factory/pizzas/CheesePizza.js: -------------------------------------------------------------------------------- 1 | import Pizza from '../Pizza'; 2 | 3 | class CheesePizza extends Pizza { 4 | constructor(style, ingredientFactory) { 5 | super({ 6 | name: style + ' Cheese Pizza' 7 | }); 8 | console.log(this.name); 9 | this.ingredientFactory = ingredientFactory; 10 | } 11 | 12 | prepare() { 13 | let ingredientFactory = this.ingredientFactory; 14 | console.log("Preparing " + this.name); 15 | this.dough = ingredientFactory.createDough(); 16 | this.sauce = ingredientFactory.createSauce(); 17 | this.cheese = ingredientFactory.createCheese(); 18 | } 19 | } 20 | 21 | export default CheesePizza; -------------------------------------------------------------------------------- /Chapter05/005_Abstract_Factory/pizzas/ClamPizza.js: -------------------------------------------------------------------------------- 1 | import Pizza from '../Pizza'; 2 | 3 | class ClamPizza extends Pizza { 4 | constructor(style, ingredientFactory) { 5 | super({ 6 | name: style + ' Clams Pizza' 7 | }); 8 | this.ingredientFactory = ingredientFactory; 9 | } 10 | 11 | prepare() { 12 | let ingredientFactory = this.ingredientFactory; 13 | console.log("Preparing " + this.name); 14 | this.dough = ingredientFactory.createDough(); 15 | this.sauce = ingredientFactory.createSauce(); 16 | this.cheese = ingredientFactory.createCheese(); 17 | this.clams = ingredientFactory.createClam(); 18 | } 19 | } 20 | 21 | export default ClamPizza; -------------------------------------------------------------------------------- /Chapter05/005_Abstract_Factory/stores/NewYorkPizzaStore.js: -------------------------------------------------------------------------------- 1 | import PizzaStore from '../../../common/PizzaStore'; 2 | import NewYorkPizzaIngredientFactory from '../ingredientFactory/NewYorkPizzaIngredientFactory'; 3 | import CheesePizza from '../pizzas/CheesePizza'; 4 | import ClamPizza from '../pizzas/ClamPizza'; 5 | 6 | const PIZZAS = { 7 | cheese: CheesePizza, 8 | clam: ClamPizza 9 | }; 10 | 11 | class NewYorkPizzaStore extends PizzaStore { 12 | createPizza(type) { 13 | let ingredientFactory = new NewYorkPizzaIngredientFactory(); 14 | let PizzaConstructor = PIZZAS[type]; 15 | let pizza = null; 16 | if (PizzaConstructor) { 17 | pizza = new PizzaConstructor('New York Style', ingredientFactory); 18 | } 19 | return pizza; 20 | } 21 | } 22 | 23 | export default NewYorkPizzaStore; -------------------------------------------------------------------------------- /Chapter05/006_Builder/Item.js: -------------------------------------------------------------------------------- 1 | class Item { 2 | get name() { 3 | throw new Error('This method should be implemented'); 4 | } 5 | get packing() { 6 | throw new Error('This method should be implemented'); 7 | } 8 | get price() { 9 | throw new Error('This method should be implemented'); 10 | } 11 | } 12 | 13 | export default Item; -------------------------------------------------------------------------------- /Chapter05/006_Builder/Main.js: -------------------------------------------------------------------------------- 1 | import MealBuilder from './MealBuilder'; 2 | 3 | var mealBuilder = new MealBuilder(); 4 | 5 | var veganMeal = mealBuilder.prepareVeganMeal(); 6 | 7 | veganMeal.showItems(); 8 | console.log(`Vegan meal cost: $${veganMeal.getCost()}`); 9 | 10 | var nonVeganMeal = mealBuilder.prepareNonVeganMeal(); 11 | 12 | nonVeganMeal.showItems(); 13 | console.log(`Non vegan meal cost: $${nonVeganMeal.getCost()}`); 14 | 15 | var deluxeMeal = mealBuilder.prepareDeluxeMeal(); 16 | 17 | deluxeMeal.showItems(); 18 | console.log(`Deluxe meal cost: $${deluxeMeal.getCost()}`); 19 | 20 | -------------------------------------------------------------------------------- /Chapter05/006_Builder/Meal.js: -------------------------------------------------------------------------------- 1 | const itemList = new WeakMap(); 2 | class Meal { 3 | get list() { 4 | if(!itemList.get(this)) { 5 | itemList.set(this, []); 6 | } 7 | return itemList.get(this); 8 | } 9 | addItem(item) { 10 | this.list.push(item); 11 | } 12 | getCost() { 13 | return this.list.reduce((accum, item) => { 14 | accum += item.price; 15 | return accum; 16 | }, 0); 17 | } 18 | showItems() { 19 | this.list.forEach((item) => { 20 | console.log(`Item: ${item.name}, Packing: ${item.packing.pack()}, Price: $${item.price}`); 21 | }); 22 | } 23 | } 24 | 25 | export default Meal; -------------------------------------------------------------------------------- /Chapter05/006_Builder/MealBuilder.js: -------------------------------------------------------------------------------- 1 | import Meal from './Meal'; 2 | import VeganBurger from './items/burgers/VeganBurger'; 3 | import BeefBurger from './items/burgers/BeefBurger'; 4 | import KobeBurger from './items/burgers/KobeBurger'; 5 | import Water from './items/drinks/Water'; 6 | import Coke from './items/drinks/Coke'; 7 | import Champagne from './items/drinks/Champagne'; 8 | import Salad from './items/side-dishes/Salad'; 9 | import Fries from './items/side-dishes/Fries'; 10 | import Crudettes from './items/side-dishes/Crudettes'; 11 | 12 | class MealBuilder { 13 | prepareVeganMeal() { 14 | var meal = new Meal(); 15 | meal.addItem(new VeganBurger()); 16 | meal.addItem(new Water()); 17 | meal.addItem(new Salad()); 18 | return meal; 19 | } 20 | prepareNonVeganMeal() { 21 | var meal = new Meal(); 22 | meal.addItem(new BeefBurger()); 23 | meal.addItem(new Coke()); 24 | meal.addItem(new Fries()); 25 | return meal; 26 | } 27 | prepareDeluxeMeal() { 28 | var meal = new Meal(); 29 | meal.addItem(new KobeBurger()); 30 | meal.addItem(new Champagne()); 31 | meal.addItem(new Crudettes()); 32 | return meal; 33 | } 34 | } 35 | 36 | export default MealBuilder; -------------------------------------------------------------------------------- /Chapter05/006_Builder/Packing.js: -------------------------------------------------------------------------------- 1 | class Packing { 2 | pack() { 3 | throw new Error('This method should be implemented'); 4 | } 5 | } 6 | 7 | export default Packing; -------------------------------------------------------------------------------- /Chapter05/006_Builder/items/Burger.js: -------------------------------------------------------------------------------- 1 | import Wrapper from '../packing/Wrapper'; 2 | import Item from '../Item'; 3 | 4 | class Burger extends Item { 5 | get packing() { 6 | return new Wrapper(); 7 | } 8 | } 9 | 10 | export default Burger; -------------------------------------------------------------------------------- /Chapter05/006_Builder/items/Drink.js: -------------------------------------------------------------------------------- 1 | import Bottle from '../packing/Bottle'; 2 | import Item from '../Item'; 3 | 4 | class Drink extends Item { 5 | get packing() { 6 | return new Bottle(); 7 | } 8 | } 9 | 10 | export default Drink; -------------------------------------------------------------------------------- /Chapter05/006_Builder/items/SideDishes.js: -------------------------------------------------------------------------------- 1 | import BoxUp from '../packing/BoxUp'; 2 | import Item from '../Item'; 3 | 4 | class SideDishes extends Item { 5 | get packing() { 6 | return new BoxUp(); 7 | } 8 | } 9 | 10 | export default SideDishes; -------------------------------------------------------------------------------- /Chapter05/006_Builder/items/burgers/BeefBurger.js: -------------------------------------------------------------------------------- 1 | import Burger from '../Burger'; 2 | 3 | class BeefBurger extends Burger { 4 | get price() { 5 | return 13; 6 | } 7 | get name() { 8 | return 'Beef Burger'; 9 | } 10 | } 11 | 12 | export default BeefBurger; -------------------------------------------------------------------------------- /Chapter05/006_Builder/items/burgers/KobeBurger.js: -------------------------------------------------------------------------------- 1 | import Burger from '../Burger'; 2 | 3 | class KobeBurger extends Burger { 4 | get price() { 5 | return 235; 6 | } 7 | get name() { 8 | return 'Kobe Burger'; 9 | } 10 | } 11 | 12 | export default KobeBurger; -------------------------------------------------------------------------------- /Chapter05/006_Builder/items/burgers/VeganBurger.js: -------------------------------------------------------------------------------- 1 | import Burger from '../Burger'; 2 | 3 | class VeganBurger extends Burger { 4 | get price() { 5 | return 16; 6 | } 7 | get name() { 8 | return 'Vegan Burger'; 9 | } 10 | } 11 | 12 | export default VeganBurger; -------------------------------------------------------------------------------- /Chapter05/006_Builder/items/drinks/Champagne.js: -------------------------------------------------------------------------------- 1 | import Drink from '../Drink'; 2 | 3 | class Champagne extends Drink { 4 | get price() { 5 | return 90; 6 | } 7 | get name() { 8 | return 'Champagne'; 9 | } 10 | } 11 | 12 | export default Champagne; -------------------------------------------------------------------------------- /Chapter05/006_Builder/items/drinks/Coke.js: -------------------------------------------------------------------------------- 1 | import Drink from '../Drink'; 2 | 3 | class Coke extends Drink { 4 | get price() { 5 | return 3.5; 6 | } 7 | get name() { 8 | return 'Coke'; 9 | } 10 | } 11 | 12 | export default Coke; -------------------------------------------------------------------------------- /Chapter05/006_Builder/items/drinks/Water.js: -------------------------------------------------------------------------------- 1 | import Drink from '../Drink'; 2 | 3 | class Water extends Drink { 4 | get price() { 5 | return 2.5; 6 | } 7 | get name() { 8 | return 'Water'; 9 | } 10 | } 11 | 12 | export default Water; -------------------------------------------------------------------------------- /Chapter05/006_Builder/items/side-dishes/Crudettes.js: -------------------------------------------------------------------------------- 1 | import SideDishes from '../SideDishes'; 2 | 3 | class Crudettes extends SideDishes { 4 | get price() { 5 | return 4; 6 | } 7 | get name() { 8 | return 'Crudettes'; 9 | } 10 | } 11 | 12 | export default Crudettes; -------------------------------------------------------------------------------- /Chapter05/006_Builder/items/side-dishes/Fries.js: -------------------------------------------------------------------------------- 1 | import SideDishes from '../SideDishes'; 2 | 3 | class Fries extends SideDishes { 4 | get price() { 5 | return 2; 6 | } 7 | get name() { 8 | return 'Fries'; 9 | } 10 | } 11 | 12 | export default Fries; -------------------------------------------------------------------------------- /Chapter05/006_Builder/items/side-dishes/Salad.js: -------------------------------------------------------------------------------- 1 | import SideDishes from '../SideDishes'; 2 | 3 | class Salad extends SideDishes { 4 | get price() { 5 | return 6; 6 | } 7 | get name() { 8 | return 'Salad'; 9 | } 10 | } 11 | 12 | export default Salad; -------------------------------------------------------------------------------- /Chapter05/006_Builder/packing/Bottle.js: -------------------------------------------------------------------------------- 1 | import Packing from '../Packing'; 2 | 3 | class Bottle extends Packing { 4 | pack() { 5 | return 'Bottle'; 6 | } 7 | } 8 | 9 | export default Bottle; -------------------------------------------------------------------------------- /Chapter05/006_Builder/packing/BoxUp.js: -------------------------------------------------------------------------------- 1 | import Packing from '../Packing'; 2 | 3 | class BoxUp extends Packing { 4 | pack() { 5 | return 'Boxing'; 6 | } 7 | } 8 | 9 | export default BoxUp; -------------------------------------------------------------------------------- /Chapter05/006_Builder/packing/Wrapper.js: -------------------------------------------------------------------------------- 1 | import Packing from '../Packing'; 2 | 3 | class Wrapper extends Packing { 4 | pack() { 5 | return 'Wrapping'; 6 | } 7 | } 8 | 9 | export default Wrapper; -------------------------------------------------------------------------------- /Chapter05/B09381_05_0.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Mastering-JavaScript-Design-Patterns-Third-Edition/a43051f47d39ae122009f70c611c13fb83907daa/Chapter05/B09381_05_0.js -------------------------------------------------------------------------------- /Chapter06/001_Adapter/Duck.js: -------------------------------------------------------------------------------- 1 | class Duck { 2 | fly() { 3 | throw new Error('This method must be overwritten!'); 4 | } 5 | 6 | quack() { 7 | throw new Error('This method must be overwritten!'); 8 | } 9 | } 10 | 11 | export default Duck; -------------------------------------------------------------------------------- /Chapter06/001_Adapter/MallardDuck.js: -------------------------------------------------------------------------------- 1 | import Duck from './Duck'; 2 | 3 | class MallardDuck extends Duck { 4 | fly() { 5 | console.log('Can fly long distances!'); 6 | } 7 | 8 | quack() { 9 | console.log('Quack! Quack!'); 10 | } 11 | } 12 | 13 | export default MallardDuck; -------------------------------------------------------------------------------- /Chapter06/001_Adapter/Turkey.js: -------------------------------------------------------------------------------- 1 | class Turkey { 2 | fly() { 3 | throw new Error('This method must be overwritten!'); 4 | } 5 | 6 | gobble() { 7 | throw new Error('This method must be overwritten'); 8 | } 9 | } 10 | 11 | export default Turkey; -------------------------------------------------------------------------------- /Chapter06/001_Adapter/TurkeyAdapter.js: -------------------------------------------------------------------------------- 1 | const MAX_FLIES = 5; 2 | 3 | class TurkeyAdapter extends Duck { 4 | constructor(oTurkey) { 5 | super(oTurkey); 6 | this.oTurkey = oTurkey; 7 | } 8 | 9 | fly() { 10 | for (let index = 0; index < MAX_FLIES; index++) { 11 | this.oTurkey.fly(); 12 | } 13 | } 14 | 15 | quack() { 16 | this.oTurkey.gobble(); 17 | } 18 | } -------------------------------------------------------------------------------- /Chapter06/001_Adapter/WildTurkey.js: -------------------------------------------------------------------------------- 1 | import Turkey from './Turkey'; 2 | 3 | class WildTurkey extends Turkey { 4 | fly() { 5 | console.log('Fly short distance!'); 6 | } 7 | 8 | gobble() { 9 | console.log('Gobble!, Gobble!'); 10 | } 11 | } 12 | 13 | export default WildTurkey; -------------------------------------------------------------------------------- /Chapter06/001_Adapter/main.js: -------------------------------------------------------------------------------- 1 | import MallardDuck from './MallardDuck'; 2 | import WildTurkey from './WildTurkey'; 3 | import TurkeyAdapter from './TurkeyAdapter'; 4 | 5 | let oMallardDuck = new MallardDuck(); 6 | let oWildTurkey = new WildTurkey(); 7 | let oTurkeyAdapter = new TurkeyAdapter(oWildTurkey); 8 | 9 | oMallardDuck.fly(); 10 | oMallardDuck.quack(); 11 | 12 | oWildTurkey.fly(); 13 | oWildTurkey.gobble(); 14 | 15 | oTurkeyAdapter.fly(); 16 | oTurkeyAdapter.quack(); 17 | -------------------------------------------------------------------------------- /Chapter06/002_Composite/CafeMenu.js: -------------------------------------------------------------------------------- 1 | import Menu from './Menu'; 2 | 3 | class CafeMenu extends Menu {} 4 | 5 | export default CafeMenu; -------------------------------------------------------------------------------- /Chapter06/002_Composite/LunchMenu.js: -------------------------------------------------------------------------------- 1 | import Menu from './Menu'; 2 | 3 | class LunchMenu extends Menu {} 4 | 5 | export default LunchMenu; -------------------------------------------------------------------------------- /Chapter06/002_Composite/Mattress.js: -------------------------------------------------------------------------------- 1 | class Mattress { 2 | constructor(aMenus) { 3 | this.menus = aMenus; 4 | } 5 | 6 | printMenu() { 7 | this.menus.print(); 8 | } 9 | } 10 | 11 | export default Mattress; -------------------------------------------------------------------------------- /Chapter06/002_Composite/Menu.js: -------------------------------------------------------------------------------- 1 | import MenuComponent from './MenuComponent'; 2 | 3 | class Menu extends MenuComponent { 4 | constructor(name, description) { 5 | super(); 6 | this.menuComponents = []; 7 | this.name = name; 8 | this.description = description; 9 | this.createIterator = function() { 10 | throw new Error('This method must be overwritten!'); 11 | } 12 | } 13 | 14 | add(menuComponent) { 15 | this.menuComponents.push(menuComponent); 16 | } 17 | 18 | remove(menuComponent) { 19 | this.menuComponents = this.menuComponents.filter(component => { 20 | return component !== component; 21 | }); 22 | } 23 | 24 | getChild(index) { 25 | return this.menuComponents[index]; 26 | } 27 | 28 | getName() { 29 | return this.name; 30 | } 31 | 32 | getDescription() { 33 | return this.description; 34 | } 35 | 36 | print() { 37 | console.log(this.getName() + ": " + this.getDescription()); 38 | console.log("--------------------------------------------"); 39 | this.menuComponents.forEach(component => { 40 | component.print(); 41 | }); 42 | } 43 | } 44 | 45 | export default Menu; 46 | -------------------------------------------------------------------------------- /Chapter06/002_Composite/MenuComponent.js: -------------------------------------------------------------------------------- 1 | class MenuComponent { 2 | constructor(name = '', description = '', isVegetarian = false, price = 0) { 3 | this.name = name; 4 | this.description = description; 5 | this._isVegetarian = isVegetarian; 6 | this.price = price; 7 | } 8 | 9 | getName() { 10 | return this.name; 11 | } 12 | 13 | getDescription() { 14 | return this.description; 15 | } 16 | 17 | getPrice() { 18 | return this.price; 19 | } 20 | 21 | isVegetarian() { 22 | return this._isVegetarian; 23 | } 24 | 25 | print() { 26 | shouldBeOverwritten(); 27 | } 28 | 29 | add() { 30 | shouldBeOverwritten(); 31 | } 32 | 33 | remove() { 34 | shouldBeOverwritten(); 35 | } 36 | 37 | getChild() { 38 | shouldBeOverwritten(); 39 | } 40 | } 41 | 42 | export default MenuComponent; -------------------------------------------------------------------------------- /Chapter06/002_Composite/MenuItem.js: -------------------------------------------------------------------------------- 1 | import MenuComponent from './MenuComponent'; 2 | 3 | class MenuItem extends MenuComponent { 4 | print() { 5 | console.log(this.getName() + ": " + this.getDescription() + ", " + this.getPrice() + "euros"); 6 | } 7 | } 8 | 9 | export default MenuItem; -------------------------------------------------------------------------------- /Chapter06/002_Composite/PancakeHouseMenu.js: -------------------------------------------------------------------------------- 1 | import Menu from './Menu'; 2 | 3 | class PancakeHouseMenu extends Menu {} 4 | 5 | export default PancakeHouseMenu; -------------------------------------------------------------------------------- /Chapter06/002_Composite/main.js: -------------------------------------------------------------------------------- 1 | import Menu from './Menu'; 2 | import MenuItem from './MenuItem'; 3 | import Mattress from './Mattress'; 4 | 5 | let oPanCakeHouseMenu = new Menu("Pancake House Menu", "Breakfast"); 6 | let oLunchMenu = new Menu("Lunch Menu", "Lunch"); 7 | let oCoffeeMenu = new Menu("Cafe Menu", "Dinner"); 8 | let oAllMenus = new Menu("ALL MENUS", "All menus combined"); 9 | 10 | oAllMenus.add(oPanCakeHouseMenu); 11 | oAllMenus.add(oLunchMenu); 12 | 13 | oLunchMenu.add(new MenuItem("Pasta", "Spaghetti with Marinara Sauce, and a slice of sourdough bread", true, 3.89)); 14 | oLunchMenu.add(oCoffeeMenu); 15 | 16 | oCoffeeMenu.add(new MenuItem("Express", "Coffee from machine", false, 0.99)); 17 | 18 | let oMattress = new Mattress(oAllMenus); 19 | console.log("---------------------------------------------"); 20 | oMattress.printMenu(); 21 | console.log("---------------------------------------------"); -------------------------------------------------------------------------------- /Chapter06/003_Decorator/Beverage.js: -------------------------------------------------------------------------------- 1 | class Beverage { 2 | constructor(description = 'Unknown beverage') { 3 | this.description = description; 4 | } 5 | 6 | getDescription() { 7 | return this.description; 8 | } 9 | 10 | cost() { 11 | throw new Error("This method must be overwritten!"); 12 | } 13 | } 14 | 15 | export default Beverage; -------------------------------------------------------------------------------- /Chapter06/003_Decorator/CondimentDecorator.js: -------------------------------------------------------------------------------- 1 | import Beverage from './Beverage'; 2 | 3 | class CondimentDecorator extends Beverage {} 4 | 5 | export default CondimentDecorator; -------------------------------------------------------------------------------- /Chapter06/003_Decorator/Expresso.js: -------------------------------------------------------------------------------- 1 | import Beverage from './Beverage'; 2 | 3 | class Espresso extends Beverage { 4 | cost() { 5 | return 1.99; 6 | } 7 | } 8 | 9 | export default Expresso; -------------------------------------------------------------------------------- /Chapter06/003_Decorator/HouseBlend.js: -------------------------------------------------------------------------------- 1 | import Beverage from './Beverage'; 2 | 3 | class HouseBlend extends Beverage { 4 | cost() { 5 | return 0.89; 6 | } 7 | } 8 | 9 | export default HouseBlend; -------------------------------------------------------------------------------- /Chapter06/003_Decorator/Mocha.js: -------------------------------------------------------------------------------- 1 | import CondimentDecorator from './CondimentDecorator'; 2 | 3 | class Mocha extends CondimentDecorator { 4 | constructor(beverage) { 5 | super(); 6 | this.beverage = beverage; 7 | } 8 | 9 | getDescription() { 10 | return this.beverage.getDescription() + ", Mocha"; 11 | } 12 | 13 | cost() { 14 | return 0.20 + this.beverage.cost(); 15 | } 16 | } 17 | 18 | export default Mocha; -------------------------------------------------------------------------------- /Chapter06/003_Decorator/Whip.js: -------------------------------------------------------------------------------- 1 | import CondimentDecorator from './CondimentDecorator'; 2 | 3 | class Whip extends CondimentDecorator { 4 | constructor(beverage) { 5 | super(); 6 | this.beverage = beverage; 7 | } 8 | 9 | getDescription() { 10 | return this.beverage.getDescription() + ', Whip'; 11 | } 12 | 13 | cost() { 14 | return 0.60 + this.beverage.cost(); 15 | } 16 | } 17 | 18 | export default Whip; -------------------------------------------------------------------------------- /Chapter06/003_Decorator/main.js: -------------------------------------------------------------------------------- 1 | import Expresso from './Expresso'; 2 | import Mocha from './Mocha'; 3 | import Whip from './Whip'; 4 | 5 | let oEspressoWithMochaAndWhip = new Espresso(); 6 | oEspressoWithMochaAndWhip = new Mocha(oEspressoWithMochaAndWhip); 7 | oEspressoWithMochaAndWhip = new Whip(oEspressoWithMochaAndWhip); 8 | 9 | console.log(oEspressoWithMochaAndWhip.cost()); -------------------------------------------------------------------------------- /Chapter06/004_Bridge/Adventure4x4Car.js: -------------------------------------------------------------------------------- 1 | import Car from './Car'; 2 | 3 | class Adventure4x4Car extends Car { 4 | constructor(color) { 5 | super('4x4 Adventure car', 'For people that does not care about existing paths', 55000, 2, color); 6 | } 7 | } 8 | 9 | export default Adventure4x4Car; -------------------------------------------------------------------------------- /Chapter06/004_Bridge/Car.js: -------------------------------------------------------------------------------- 1 | class Car { 2 | constructor(name, description, price, places = 2, color, brand = 'Cartisfaction') { 3 | this.brand = brand; 4 | this.name = name; 5 | this.description = description; 6 | this.price = price; 7 | this.places = places; 8 | this.color = color; 9 | } 10 | applyColor() { 11 | console.log(`${this.name} car painted with color ${this.color.applyColor()}`); 12 | } 13 | } 14 | 15 | export default Car; -------------------------------------------------------------------------------- /Chapter06/004_Bridge/Color.js: -------------------------------------------------------------------------------- 1 | class Color { 2 | applyColor() { 3 | throw new Error("This method should be overwritten"); 4 | } 5 | } 6 | 7 | export default Color; -------------------------------------------------------------------------------- /Chapter06/004_Bridge/FamilyCar.js: -------------------------------------------------------------------------------- 1 | import Car from './Car'; 2 | 3 | class FamilyCar extends Car { 4 | constructor(color) { 5 | super('Family car', 'Enjoy with your family', 30000, 5, color); 6 | } 7 | } 8 | 9 | export default FamilyCar; -------------------------------------------------------------------------------- /Chapter06/004_Bridge/GreyColor.js: -------------------------------------------------------------------------------- 1 | import Color from './Color'; 2 | 3 | class GreyColor extends Color { 4 | applyColor() { 5 | return 'grey'; 6 | } 7 | } 8 | 9 | export default GreyColor; -------------------------------------------------------------------------------- /Chapter06/004_Bridge/MatteBlackColor.js: -------------------------------------------------------------------------------- 1 | import Color from './Color'; 2 | 3 | class MatteBlackColor extends Color { 4 | applyColor() { 5 | return 'matte black'; 6 | } 7 | } 8 | 9 | export default MatteBlackColor; -------------------------------------------------------------------------------- /Chapter06/004_Bridge/RedColor.js: -------------------------------------------------------------------------------- 1 | import Color from './Color'; 2 | 3 | class RedColor extends Color { 4 | applyColor() { 5 | return 'red'; 6 | } 7 | } 8 | 9 | export default RedColor; -------------------------------------------------------------------------------- /Chapter06/004_Bridge/UrbanCar.js: -------------------------------------------------------------------------------- 1 | import Car from './Car'; 2 | 3 | class UrbanCar extends Car { 4 | constructor(color) { 5 | super('Urban car', 'Small and designed for the city', 12000, 2, color); 6 | } 7 | } 8 | 9 | export default UrbanCar; -------------------------------------------------------------------------------- /Chapter06/004_Bridge/main.js: -------------------------------------------------------------------------------- 1 | const familyCar = new FamilyCar(new GreyColor()); 2 | const adventureCar = new Adventure4x4Car(new MatteBlackColor()); 3 | const urbanCar = new UrbanCar(new RedColor()); 4 | 5 | familyCar.applyColor(); 6 | adventureCar.applyColor(); 7 | urbanCar.applyColor(); -------------------------------------------------------------------------------- /Chapter06/005_Facade/Amplifier.js: -------------------------------------------------------------------------------- 1 | import Switchable from './Switchable'; 2 | import DummyConstructor from './DummyConstructor'; 3 | 4 | class Amplifier extends Switchable(DummyConstructor) { 5 | constructor() { 6 | super(); 7 | this.volume = 0; 8 | this.brPlayer = null; 9 | this.mp3Player = null; 10 | this.tuner = null; 11 | this.surroundSound = false; 12 | this.stereoSound = false; 13 | } 14 | 15 | on() { 16 | console.log("Amplifier is on!"); 17 | } 18 | 19 | off() { 20 | console.log("Amplifier is off!"); 21 | } 22 | 23 | setVolume(volume) { 24 | this.volume = volume; 25 | console.log("Volume change to " + volume); 26 | } 27 | 28 | setBrPlayer(brPlayer) { 29 | this.brPlayer = brPlayer; 30 | console.log("Plugged BlueRay Player to Amplifier!"); 31 | } 32 | 33 | setMP3Player(mp3Player) { 34 | this.mp3Player = mp3Player; 35 | console.log("Plugged MP3 Player to Amplifier!"); 36 | } 37 | 38 | setTuner(tuner) { 39 | this.tuner = tuner; 40 | console.log("Plugged on Tuner to Amplifier!"); 41 | } 42 | 43 | setSurroundSound() { 44 | this.surroundSound = true; 45 | console.log("Surround Mode is active!"); 46 | } 47 | 48 | setStereoSound() { 49 | this.stereoSound = true; 50 | console.log("Stereo Mode is active!"); 51 | } 52 | } 53 | 54 | export default Amplifier; -------------------------------------------------------------------------------- /Chapter06/005_Facade/BrPlayer.js: -------------------------------------------------------------------------------- 1 | import Switchable from './Switchable'; 2 | import Playable from './Playable'; 3 | import DummyConstructor from './DummyConstructor'; 4 | 5 | class BrPlayer extends Switchable(Playable(DummyConstructor)) { 6 | on() { 7 | console.log("BRPlayer is on!"); 8 | } 9 | 10 | off() { 11 | console.log("BRPlayer is off!"); 12 | } 13 | 14 | eject() { 15 | console.log("Eject BR!"); 16 | } 17 | 18 | play(movie) { 19 | console.log("Playing " + movie.name); 20 | } 21 | 22 | stop() { 23 | console.log("Stop BRPlayer!"); 24 | } 25 | } 26 | 27 | export default BrPlayer; -------------------------------------------------------------------------------- /Chapter06/005_Facade/DummyConstructor.js: -------------------------------------------------------------------------------- 1 | const DummyConstructor = Object.constructor; 2 | 3 | export default DummyConstructor; -------------------------------------------------------------------------------- /Chapter06/005_Facade/HomeTheaterFacade.js: -------------------------------------------------------------------------------- 1 | class HomeTheaterFacade { 2 | constructor({ amplifier = null, tuner = null, brPlayer = null, mp3Player = null, projector = null, theaterLights = null, screen = null, popcornPopper = null}) { 3 | this.amplifier = amplifier; 4 | this.tuner = tuner; 5 | this.brPlayer = brPlayer; 6 | this.mp3Player = mp3Player; 7 | this.projector = projector; 8 | this.theaterLights = theaterLights; 9 | this.screen = screen; 10 | this.popcornPopper = popcornPopper; 11 | } 12 | 13 | watchMovie(movie) { 14 | console.log('Get ready to watch a movie...'); 15 | 16 | this.popcornPopper.on(); 17 | this.popcornPopper.pop(); 18 | 19 | this.theaterLights.off(); 20 | 21 | this.screen.down(); 22 | 23 | this.projector.on(); 24 | this.projector.setWideScreenMode(); 25 | 26 | this.amplifier.on(); 27 | this.amplifier.setBrPlayer(this.dvdPlayer); 28 | this.amplifier.setSurroundSound(); 29 | this.amplifier.setVolume(5); 30 | 31 | this.brPlayer.on(); 32 | this.brPlayer.play(movie); 33 | } 34 | 35 | endMovie() { 36 | console.log("Shutting movie theater down..."); 37 | this.popcornPopper.off(); 38 | 39 | this.theaterLights.on(); 40 | 41 | this.screen.up(); 42 | 43 | this.projector.off(); 44 | 45 | this.amplifier.off(); 46 | 47 | this.brPlayer.stop(); 48 | this.brPlayer.eject(); 49 | this.brPlayer.off(); 50 | } 51 | 52 | listenToMP3(playlist) { 53 | console.log("Start listening your music..."); 54 | 55 | this.amplifier.on(); 56 | this.amplifier.setCdPlayer(this.cdPlayer); 57 | this.amplifier.setStereoSound(); 58 | this.amplifier.setVolume(5); 59 | 60 | this.mp3Player.on(); 61 | this.mp3Player.play(playlist); 62 | } 63 | 64 | endMP3() { 65 | console.log("End listening your music or the playlist has finished!"); 66 | 67 | this.amplifier.off(); 68 | 69 | this.mp3Player.stop(); 70 | this.mp3Player.off(); 71 | } 72 | 73 | listenToRadio() { 74 | console.log("Start listening your favorite radio station..."); 75 | 76 | this.amplifier.on(); 77 | this.amplifier.setTuner(this.tuner); 78 | this.amplifier.setStereoSound(); 79 | this.amplifier.setVolume(5); 80 | 81 | this.tuner.on(); 82 | this.tuner.setFm(); 83 | this.tuner.setFrequency(90.9); 84 | } 85 | 86 | endRadio() { 87 | console.log("End listening your favorite radio station..."); 88 | 89 | this.amplifier.off(); 90 | 91 | this.tuner.off(); 92 | } 93 | } 94 | 95 | export default HomeTheaterFacade; -------------------------------------------------------------------------------- /Chapter06/005_Facade/Movie.js: -------------------------------------------------------------------------------- 1 | class Movie { 2 | constructor(name = '', minutes = 0, director = '', actors = [], description = '') { 3 | this.name = name; 4 | this.minutes = minutes; 5 | this.director = director; 6 | this.actors = actors; 7 | this.description = description; 8 | } 9 | 10 | setName(name) { 11 | this.name = name; 12 | } 13 | 14 | setMinutes(minutes) { 15 | this.minutes = minutes; 16 | } 17 | 18 | setDirector(director) { 19 | this.director = director; 20 | } 21 | 22 | setActors(actors) { 23 | this.actors = actors; 24 | } 25 | 26 | setDescription(description) { 27 | this.description = description; 28 | } 29 | } 30 | 31 | export default Movie; -------------------------------------------------------------------------------- /Chapter06/005_Facade/Mp3Player.js: -------------------------------------------------------------------------------- 1 | import Switchable from './Switchable'; 2 | import Playable from './Playable'; 3 | import DummyConstructor from './DummyConstructor'; 4 | 5 | class Mp3Player extends Switchable(Playable(DummyConstructor)) { 6 | on() { 7 | console.log("Mp3Player is on!"); 8 | } 9 | 10 | off() { 11 | console.log("Mp3Player is off!"); 12 | } 13 | 14 | play(playlist) { 15 | console.log("Playing " + playlist.sName); 16 | } 17 | 18 | stop() { 19 | console.log("Stop MP3Player!"); 20 | } 21 | } 22 | 23 | export default Mp3Player; -------------------------------------------------------------------------------- /Chapter06/005_Facade/Nikita.js: -------------------------------------------------------------------------------- 1 | import Movie from './Movie'; 2 | 3 | class Nikita extends Movie { 4 | constructor() { 5 | super('Nikita, hard to kill!', 6 | 120, 7 | 'Steven Spielberg', 8 | ['Brad Pitt'], 9 | 'Bloody!' 10 | ); 11 | } 12 | } 13 | 14 | export default Nikita; -------------------------------------------------------------------------------- /Chapter06/005_Facade/Playable.js: -------------------------------------------------------------------------------- 1 | const Playable = Sup => class extends Sup { 2 | eject() { 3 | throw new Error('This method should be overwritten!'); 4 | } 5 | 6 | play() { 7 | throw new Error('This method should be overwritten!'); 8 | } 9 | 10 | stop() { 11 | throw new Error('This method should be overwritten!'); 12 | } 13 | }; 14 | 15 | export default Playable; -------------------------------------------------------------------------------- /Chapter06/005_Facade/PopcornPopper.js: -------------------------------------------------------------------------------- 1 | import Switchable from './Switchable'; 2 | import DummyConstructor from './DummyConstructor'; 3 | 4 | class PopcornPopper extends Switchable(DummyConstructor) { 5 | on() { 6 | console.log("PopcornPopper is on!"); 7 | } 8 | 9 | off() { 10 | console.log("PopcornPopper is off!"); 11 | } 12 | 13 | pop() { 14 | console.log("Yum!Yum!"); 15 | } 16 | } 17 | 18 | export default PopcornPopper; -------------------------------------------------------------------------------- /Chapter06/005_Facade/Projector.js: -------------------------------------------------------------------------------- 1 | import Switchable from './Switchable'; 2 | import DummyConstructor from './DummyConstructor'; 3 | 4 | class Projector extends Switchable(DummyConstructor) { 5 | constructor() { 6 | super(); 7 | this.wideScreenMode = false; 8 | } 9 | 10 | on() { 11 | console.log("Projector is on!"); 12 | } 13 | 14 | off() { 15 | console.log("Projector is off!"); 16 | } 17 | 18 | setWideScreenMode() { 19 | this.wideScreenMode = true; 20 | console.log("Projector now is on wide screen mode!"); 21 | } 22 | } 23 | 24 | export default Projector; -------------------------------------------------------------------------------- /Chapter06/005_Facade/Screen.js: -------------------------------------------------------------------------------- 1 | class Screen { 2 | down() { 3 | console.log("The screen is down!"); 4 | } 5 | 6 | up() { 7 | console.log("The screen is up!"); 8 | } 9 | } 10 | 11 | export default Screen; -------------------------------------------------------------------------------- /Chapter06/005_Facade/Switchable.js: -------------------------------------------------------------------------------- 1 | const Switchable = Sup => class extends Sup { 2 | on() { 3 | throw new Error('This method should be overwritten!'); 4 | } 5 | 6 | off() { 7 | throw new Error('This method should be overwritten!'); 8 | } 9 | }; 10 | 11 | export default Switchable; -------------------------------------------------------------------------------- /Chapter06/005_Facade/TheaterLights.js: -------------------------------------------------------------------------------- 1 | import Switchable from './Switchable'; 2 | import DummyConstructor from './DummyConstructor'; 3 | 4 | class TheaterLights extends Switchable(DummyConstructor) { 5 | on() { 6 | console.log("The lights are on!"); 7 | } 8 | 9 | off() { 10 | console.log("The lights are off!"); 11 | } 12 | } 13 | 14 | export default TheaterLights; -------------------------------------------------------------------------------- /Chapter06/005_Facade/Tuner.js: -------------------------------------------------------------------------------- 1 | import Switchable from './Switchable'; 2 | import DummyConstructor from './DummyConstructor'; 3 | 4 | class Tuner extends Switchable(DummyConstructor) { 5 | constructor() { 6 | this.amplifier = null; 7 | this.frequency = 0; 8 | } 9 | 10 | on() { 11 | console.log("Tuner is on!"); 12 | } 13 | 14 | off() { 15 | console.log("Tuner is off!"); 16 | } 17 | 18 | setAm() { 19 | console.log("Tuner AM!"); 20 | } 21 | 22 | setFm() { 23 | console.log("Tuner FM!"); 24 | } 25 | 26 | setFrequency(frequency) { 27 | this.frequency = frequency; 28 | console.log("Tuner frequency changed to " + frequency); 29 | } 30 | } 31 | 32 | export default Tuner; -------------------------------------------------------------------------------- /Chapter06/005_Facade/main.js: -------------------------------------------------------------------------------- 1 | import HomeTheaterFacade from './HomeTheaterFacade'; 2 | import Amplifier from './Amplifier'; 3 | import BrPlayer from './BrPlayer'; 4 | import Mp3Player from './Mp3Player'; 5 | import Projector from './Projector'; 6 | import TheaterLights from './TheaterLights'; 7 | import Screen from './Screen'; 8 | import PopcornPopper from './PopcornPopper'; 9 | 10 | var oHomeTheaterFacade = new HomeTheaterFacade({ 11 | amplifier: new Amplifier(), 12 | brPlayer: new BrPlayer(), 13 | mp3Player: new Mp3Player(), 14 | projector: new Projector(), 15 | theaterLights: new TheaterLights(), 16 | screen: new Screen(), 17 | popcornPopper: new PopcornPopper() 18 | }); 19 | oHomeTheaterFacade.watchMovie(new Nikita()); 20 | oHomeTheaterFacade.endMovie(); -------------------------------------------------------------------------------- /Chapter06/006_Flyweight/Forest.js: -------------------------------------------------------------------------------- 1 | import Tree from './Tree'; 2 | import TreeFactory from './TreeFactory'; 3 | 4 | const privateTrees = new WeakMap(); 5 | class Forest { 6 | constructor() { 7 | privateTrees.set(this, []); 8 | } 9 | get trees() { 10 | return privateTrees.get(this); 11 | } 12 | plantTree(x, y, name, color, treeConfig) { 13 | const type = TreeFactory.getTreeType(name, color, treeConfig); 14 | const tree = new Tree(x, y, type); 15 | this.trees.push(tree); 16 | } 17 | render(canvas) { 18 | this.trees.forEach((tree) => { 19 | tree.render(canvas); 20 | }); 21 | } 22 | } 23 | 24 | export default Forest; -------------------------------------------------------------------------------- /Chapter06/006_Flyweight/Tree.js: -------------------------------------------------------------------------------- 1 | const privateX = new WeakMap(); 2 | const privateY = new WeakMap(); 3 | const privateTreeType = new WeakMap(); 4 | 5 | class Tree { 6 | constructor(x = 0, y = 0, treeType) { 7 | privateX.set(this, x); 8 | privateY.set(this, y); 9 | privateTreeType.set(this, treeType); 10 | } 11 | get x() { 12 | return privateX.get(this); 13 | } 14 | get y() { 15 | return privateY.get(this); 16 | } 17 | get treeType() { 18 | return privateTreeType.get(this); 19 | } 20 | render(canvas) { 21 | const context = canvas.getContext("2d"); 22 | this.treeType.render(context, this.x, this.y); 23 | } 24 | } 25 | 26 | export default Tree; -------------------------------------------------------------------------------- /Chapter06/006_Flyweight/TreeFactory.js: -------------------------------------------------------------------------------- 1 | import TreeType from './TreeType'; 2 | 3 | const treeTypesMap = new Map(); 4 | class TreeFactory { 5 | static getTreeType(name, color, treeConfig) { 6 | let result = treeTypesMap.get(name); 7 | if(result == null) { 8 | result = new TreeType(name, color, treeConfig); 9 | treeTypesMap.set(name, result); 10 | } 11 | return result; 12 | } 13 | } 14 | 15 | export default TreeFactory; -------------------------------------------------------------------------------- /Chapter06/006_Flyweight/TreeType.js: -------------------------------------------------------------------------------- 1 | const privateName = new WeakMap(); 2 | const privateColor = new WeakMap(); 3 | const privateTreeConfig = new WeakMap(); 4 | 5 | class TreeType { 6 | constructor(name, color, treeConfig) { 7 | privateName.set(this, name); 8 | privateColor.set(this, color); 9 | privateTreeConfig.set(this, treeConfig); 10 | } 11 | get name() { 12 | return privateName.set(this); 13 | } 14 | get color() { 15 | return privateColor.set(this); 16 | } 17 | get treeConfig() { 18 | return privateTreeConfig.set(this); 19 | } 20 | render(context, x, y) { 21 | context.fillStyle = "black"; 22 | context.fillRect(x - 1, y, 3, 5); 23 | } 24 | } 25 | 26 | export default TreeType; -------------------------------------------------------------------------------- /Chapter06/006_Flyweight/main.js: -------------------------------------------------------------------------------- 1 | import Forest from './Forest'; 2 | 3 | const CANVAS_SIZE = 600; 4 | const TREES_TO_DRAW = 99900; 5 | const TREE_TYPES = 2; 6 | const forest = new Forest(); 7 | 8 | const getAmountOfTreesToRender = (amount, types) => { 9 | return Math.floor(amount / types); 10 | }; 11 | 12 | const random = (min, max) => { 13 | return min + (Math.random() * ((max - min) + 1)); 14 | } 15 | const renderForest = (forest, canvas) => { 16 | for(let i = 0; i < getAmountOfTreesToRender(TREES_TO_DRAW, TREE_TYPES); i++) { 17 | forest.plantTree(random(0, CANVAS_SIZE), random(0, CANVAS_SIZE), 'Red Maple', 'red', 'Red Maple texture'); 18 | forest.plantTree(random(0, CANVAS_SIZE), random(0, CANVAS_SIZE), 'Gray Birch', 'gray', 'Gray Birch texture stub'); 19 | } 20 | 21 | forest.render(canvas); 22 | 23 | console.log(TREES_TO_DRAW + ' trees rendered'); 24 | console.log('Memory usage:'); 25 | console.log('Tree size (8 bytes) * ' + TREES_TO_DRAW + '+ TreeTypes size (~30 bytes) * ' + TREE_TYPES); 26 | console.log('Total: ' + ((TREES_TO_DRAW * 8 + TREE_TYPES * 30) / 1024 / 1024) + 'MB (instead of ' + ((TREES_TO_DRAW * 38) / 1024 / 1024) + 'MB)'); 27 | } 28 | 29 | renderForest(new Forest(), document.createElement('canvas')); 30 | -------------------------------------------------------------------------------- /Chapter06/007_Proxy/PublicLibrary.js: -------------------------------------------------------------------------------- 1 | class PublicLibrary { 2 | constructor(books) { 3 | this.catalog = {}; 4 | this.setCatalogFromBooks(books); 5 | } 6 | 7 | setCatalogFromBooks(books) { 8 | books.forEach(book => { 9 | this.catalog[book.getIsbn()] = { 10 | book: book, 11 | available: true 12 | }; 13 | }); 14 | } 15 | 16 | findBooks(query) { 17 | console.log("Enter findBooks PublicLibrary"); 18 | let results = []; 19 | for(let book in this.catalog) { 20 | if (query.match(book.getTitle()) || query.match(book.getAuthor())) { 21 | results.push(book); 22 | } 23 | } 24 | return results; 25 | } 26 | 27 | checkoutBook(book) { 28 | let isbn = book.getIsbn(); 29 | book = this.catalog[isbn]; 30 | if(book) { 31 | if(book.available) { 32 | book.available = false; 33 | return book; 34 | } else { 35 | throw new Error('PublicLibrary: book ' + book.getTitle() + ' is not currently available.'); 36 | } 37 | } else { 38 | throw new Error('PublicLibrary: book ' + book.getTitle() + ' not found.'); 39 | } 40 | } 41 | 42 | returnBook(book) { 43 | let isbn = book.getIsbn(); 44 | book = this.catalog[isbn]; 45 | if(book) { 46 | book.available = true; 47 | } else { 48 | throw new Error('PublicLibrary: book ' + book.getTitle() + ' not found.'); 49 | } 50 | } 51 | } 52 | 53 | export default PublicLibrary; -------------------------------------------------------------------------------- /Chapter06/007_Proxy/PublicLibraryProxy.js: -------------------------------------------------------------------------------- 1 | import PublicLibrary from './PublicLibrary'; 2 | 3 | class PublicLibraryProxy { 4 | constructor(catalog = []) { 5 | this.library = new PublicLibrary(catalog); 6 | } 7 | 8 | findBooks(query) { 9 | console.log("Enter findBooks PublicLibraryProxy"); 10 | return this.library.findBooks(query); 11 | } 12 | 13 | checkoutBook(book) { 14 | return this.library.checkoutBook(book); 15 | } 16 | 17 | returnBook(book) { 18 | return this.library.returnBook(book); 19 | } 20 | } 21 | 22 | export default PublicLibraryProxy; -------------------------------------------------------------------------------- /Chapter06/007_Proxy/main.js: -------------------------------------------------------------------------------- 1 | import PublicLibraryProxy from './PublicLibraryProxy'; 2 | 3 | var oProxyLibrary = new PublicLibraryProxy(); 4 | oProxyLibrary.findBooks('test'); -------------------------------------------------------------------------------- /Chapter06/008_VirtualProxy/PublicLibrary.js: -------------------------------------------------------------------------------- 1 | class PublicLibrary { 2 | constructor(books) { 3 | this.catalog = {}; 4 | this.setCatalogFromBooks(books); 5 | } 6 | 7 | setCatalogFromBooks(books) { 8 | books.forEach(book => { 9 | this.catalog[book.getIsbn()] = { 10 | book: book, 11 | available: true 12 | }; 13 | }); 14 | } 15 | 16 | findBooks(query) { 17 | console.log("Enter findBooks PublicLibrary"); 18 | let results = []; 19 | for(let book in this.catalog) { 20 | if (query.match(book.getTitle()) || query.match(book.getAuthor())) { 21 | results.push(book); 22 | } 23 | } 24 | return results; 25 | } 26 | 27 | checkoutBook(book) { 28 | let isbn = book.getIsbn(); 29 | book = this.catalog[isbn]; 30 | if(book) { 31 | if(book.available) { 32 | book.available = false; 33 | return book; 34 | } else { 35 | throw new Error('PublicLibrary: book ' + book.getTitle() + ' is not currently available.'); 36 | } 37 | } else { 38 | throw new Error('PublicLibrary: book ' + book.getTitle() + ' not found.'); 39 | } 40 | } 41 | 42 | returnBook(book) { 43 | let isbn = book.getIsbn(); 44 | book = this.catalog[isbn]; 45 | if(book) { 46 | book.available = true; 47 | } else { 48 | throw new Error('PublicLibrary: book ' + book.getTitle() + ' not found.'); 49 | } 50 | } 51 | } 52 | 53 | export default PublicLibrary; -------------------------------------------------------------------------------- /Chapter06/008_VirtualProxy/PublicLibraryVirtualProxy.js: -------------------------------------------------------------------------------- 1 | import { initializeLibrary } from './utils'; 2 | 3 | class PublicLibraryVirtualProxy { 4 | constructor(catalog = []) { 5 | this.library = null; 6 | this.catalog = catalog; 7 | } 8 | 9 | findBooks(query) { 10 | console.log("Enter findBooks PublicLibraryVirtualProxy"); 11 | initializeLibrary(this); 12 | return this.library.findBooks(query); 13 | } 14 | 15 | checkoutBook(book) { 16 | initializeLibrary(this); 17 | return this.library.checkoutBook(book); 18 | } 19 | 20 | returnBook(book) { 21 | initializeLibrary(this); 22 | return this.library.returnBook(book); 23 | } 24 | } 25 | 26 | export default PublicLibraryVirtualProxy; -------------------------------------------------------------------------------- /Chapter06/008_VirtualProxy/main.js: -------------------------------------------------------------------------------- 1 | import PublicLibraryVirtualProxy from './PublicLibraryVirtualProxy'; 2 | 3 | let oVirtualProxyLibrary = new PublicLibraryVirtualProxy(); 4 | oVirtualProxyLibrary.findBooks('test'); -------------------------------------------------------------------------------- /Chapter06/008_VirtualProxy/utils.js: -------------------------------------------------------------------------------- 1 | export default { 2 | initializeLibrary: (instance) => { 3 | if (instance.library === null) { 4 | instance.library = new PublicLibrary(instance.catalog); 5 | } 6 | } 7 | }; -------------------------------------------------------------------------------- /Chapter07/Chain of responsibility/ATM.js: -------------------------------------------------------------------------------- 1 | import FiftyBillDispenser from './FiftyBillDispenser'; 2 | import TwentyBillDispenser from './TwentyBillDispenser'; 3 | import TenBillDispenser from './TenBillDispenser'; 4 | 5 | class ATM { 6 | constructor() { 7 | const B50 = new FiftyBillDispenser(); 8 | const B20 = new TwentyBillDispenser(); 9 | const B10 = new TenBillDispenser(); 10 | 11 | B50.setNextChain(B20); 12 | B20.setNextChain(B10); 13 | 14 | this.chain = B50; 15 | } 16 | dispense(currency) { 17 | if(currency.amount % 10 !== 0) { 18 | throw new Error('Amount should be multiple of 10.'); 19 | } 20 | this.chain.dispense(currency); 21 | } 22 | } 23 | 24 | export default ATM; -------------------------------------------------------------------------------- /Chapter07/Chain of responsibility/Currency.js: -------------------------------------------------------------------------------- 1 | class Currency { 2 | constructor(amount, sign) { 3 | this.amount = amount; 4 | this.sign = sign; 5 | } 6 | } 7 | 8 | export default Currency; -------------------------------------------------------------------------------- /Chapter07/Chain of responsibility/DispenseChain.js: -------------------------------------------------------------------------------- 1 | class DispenseChain { 2 | setNextChain(chainElement) { 3 | throw new Error('This method should be overwritten'); 4 | } 5 | dispense(currency) { 6 | throw new Error('This method should be overwritten'); 7 | } 8 | } 9 | 10 | export default DispenseChain; -------------------------------------------------------------------------------- /Chapter07/Chain of responsibility/FiftyBillDispenser.js: -------------------------------------------------------------------------------- 1 | import DispenseChain from './DispenseChain'; 2 | 3 | class FiftyBillDispenser extends DispenseChain { 4 | setNextChain(chainElement) { 5 | this.chain = chainElement; 6 | } 7 | dispense(currency) { 8 | const amount = currency.amount; 9 | if(amount >= 50) { 10 | const billsQuantity = Math.floor(amount / 50); 11 | const remainder = amount % 50; 12 | console.log('Dispensing ' + billsQuantity + ' note' + (billsQuantity > 1 ? 's' : '') + ' of 50' + currency.sign + '.'); 13 | if(remainder > 0) { 14 | this.chain.dispense(new Currency(remainder, currency.sign)); 15 | } 16 | } else { 17 | this.chain.dispense(currency); 18 | } 19 | } 20 | } 21 | 22 | export default FiftyBillDispenser; -------------------------------------------------------------------------------- /Chapter07/Chain of responsibility/TenBillDispenser.js: -------------------------------------------------------------------------------- 1 | import DispenseChain from './DispenseChain'; 2 | 3 | class TenBillDispenser extends DispenseChain { 4 | setNextChain(chainElement) { 5 | this.chain = chainElement; 6 | } 7 | dispense(currency) { 8 | const amount = currency.amount; 9 | if(amount >= 10) { 10 | const billsQuantity = Math.floor(amount / 10); 11 | const remainder = amount % 10; 12 | console.log('Dispensing ' + billsQuantity + ' note' + (billsQuantity > 1 ? 's' : '') + ' of 10' + currency.sign + '.'); 13 | if(remainder > 0) { 14 | this.chain.dispense(new Currency(remainder, currency.sign)); 15 | } 16 | } else { 17 | this.chain.dispense(currency); 18 | } 19 | } 20 | } 21 | 22 | export default TenBillDispenser; -------------------------------------------------------------------------------- /Chapter07/Chain of responsibility/TwentyBillDispenser.js: -------------------------------------------------------------------------------- 1 | import DispenseChain from './DispenseChain'; 2 | 3 | class TwentyBillDispenser extends DispenseChain { 4 | setNextChain(chainElement) { 5 | this.chain = chainElement; 6 | } 7 | dispense(currency) { 8 | const amount = currency.amount; 9 | if(amount >= 20) { 10 | const billsQuantity = Math.floor(amount / 20); 11 | const remainder = amount % 20; 12 | console.log('Dispensing ' + billsQuantity + ' note' + (billsQuantity > 1 ? 's' : '') + ' of 20' + currency.sign + '.'); 13 | if(remainder > 0) { 14 | this.chain.dispense(new Currency(remainder, currency.sign)); 15 | } 16 | } else { 17 | this.chain.dispense(currency); 18 | } 19 | } 20 | } 21 | 22 | export default TwentyBillDispenser; -------------------------------------------------------------------------------- /Chapter07/Chain of responsibility/main.js: -------------------------------------------------------------------------------- 1 | import Currency from './Currency'; 2 | import ATM from './ATM'; 3 | 4 | const atm = new ATM(); 5 | 6 | atm.dispense(new Currency(160, '€')); -------------------------------------------------------------------------------- /Chapter07/Command/Command.js: -------------------------------------------------------------------------------- 1 | class Command { 2 | execute() { 3 | throw new Error('This method must be overwritten!'); 4 | } 5 | } 6 | 7 | export default Command; 8 | -------------------------------------------------------------------------------- /Chapter07/Command/Light.js: -------------------------------------------------------------------------------- 1 | class Light { 2 | constructor() { 3 | this._on = false; 4 | } 5 | 6 | on() { 7 | this._on = true; 8 | console.log('Light is on'); 9 | } 10 | 11 | off() { 12 | this._on = false; 13 | console.log('Light is off'); 14 | } 15 | } 16 | 17 | export default Light; -------------------------------------------------------------------------------- /Chapter07/Command/LightOnCommand.js: -------------------------------------------------------------------------------- 1 | import Command from './Command'; 2 | 3 | class LightOnCommand extends Command { 4 | constructor(light) { 5 | super(); 6 | this.light = light; 7 | } 8 | 9 | execute() { 10 | this.light.on(); 11 | } 12 | } 13 | 14 | export default LightOnCommand; -------------------------------------------------------------------------------- /Chapter07/Command/SimpleRemoteControl.js: -------------------------------------------------------------------------------- 1 | class SimpleRemoteControl { 2 | constructor() { 3 | this.command = null; 4 | } 5 | 6 | setCommand(command) { 7 | this.command = command; 8 | } 9 | 10 | buttonWasPressed() { 11 | this.command.execute(); 12 | } 13 | } 14 | 15 | export default SimpleRemoteControl; -------------------------------------------------------------------------------- /Chapter07/Command/main.js: -------------------------------------------------------------------------------- 1 | import SimpleRemoteControl from './SimpleRemoteControl'; 2 | import Light from './Light'; 3 | import LightOnCommand from './LightOnCommand'; 4 | 5 | const oSimpleRemote = new SimpleRemoteControl(); 6 | const oLight = new Light(); 7 | const oLightCommand = new LightOnCommand(oLight); 8 | 9 | oSimpleRemote.setCommand(oLightCommand); 10 | oSimpleRemote.buttonWasPressed(); -------------------------------------------------------------------------------- /Chapter07/Composite Iterator/Mattress.js: -------------------------------------------------------------------------------- 1 | class Mattress { 2 | constructor(menus) { 3 | this.menus = menus; 4 | } 5 | 6 | printMenu() { 7 | this.menus.print(); 8 | } 9 | } 10 | 11 | export default Mattress; -------------------------------------------------------------------------------- /Chapter07/Composite Iterator/Menu.js: -------------------------------------------------------------------------------- 1 | import MenuComponent from './MenuComponent'; 2 | 3 | class Menu extends MenuComponent { 4 | constructor(name, description) { 5 | super(); 6 | this.iterator = null; 7 | this.menuComponents = []; 8 | this.name = name; 9 | this.description = description; 10 | } 11 | 12 | add(menuComponent) { 13 | this.menuComponents.push(menuComponent); 14 | } 15 | 16 | remove(menuComponent) { 17 | this.menuComponents = this.menuComponents.filter(component => { 18 | return component !== menuComponent; 19 | }); 20 | } 21 | 22 | getChild(index) { 23 | return this.menuComponents[index]; 24 | } 25 | 26 | getName() { 27 | return this.name; 28 | } 29 | 30 | getDescription() { 31 | return this.description; 32 | } 33 | 34 | print() { 35 | console.log(this.getName() + ": " + this.getDescription()); 36 | console.log("--------------------------------------------"); 37 | 38 | for (let component of this.menuComponents) { 39 | component.print(); 40 | } 41 | } 42 | 43 | createIterator() { 44 | if (this.iterator === null) { 45 | this.iterator = this.menuComponents[Symbol.iterator](); 46 | } 47 | return this.iterator; 48 | }; 49 | } 50 | 51 | export default Menu; -------------------------------------------------------------------------------- /Chapter07/Composite Iterator/MenuComponent.js: -------------------------------------------------------------------------------- 1 | class MenuComponent { 2 | constructor(name, description, isVegetarian, price) { 3 | this.name = name; 4 | this.description = description; 5 | this._isVegetarian = isVegetarian; 6 | this.price = price; 7 | } 8 | 9 | isVegetarian() { 10 | return !!this._isVegetarian; 11 | } 12 | 13 | getName() { 14 | throw new Error("This method must be overwritten!"); 15 | } 16 | 17 | getDescription() { 18 | throw new Error("This method must be overwritten!"); 19 | } 20 | 21 | getPrice() { 22 | throw new Error("This method must be overwritten!"); 23 | } 24 | 25 | print() { 26 | throw new Error("This method must be overwritten!"); 27 | } 28 | 29 | add() { 30 | throw new Error("This method must be overwritten!"); 31 | } 32 | 33 | remove() { 34 | throw new Error("This method must be overwritten!"); 35 | } 36 | 37 | getChild() { 38 | throw new Error("This method must be overwritten!"); 39 | } 40 | 41 | createIterator() { 42 | throw new Error("This method must be overwritten!"); 43 | } 44 | } 45 | 46 | export default MenuComponent; -------------------------------------------------------------------------------- /Chapter07/Composite Iterator/MenuItem.js: -------------------------------------------------------------------------------- 1 | import MenuComponent from './MenuComponent'; 2 | 3 | class MenuItem extends MenuComponent { 4 | getName() { 5 | return this.name; 6 | } 7 | 8 | getDescription() { 9 | return this.description; 10 | } 11 | 12 | getPrice() { 13 | return this.price; 14 | } 15 | 16 | print() { 17 | console.log(this.getName() + ": " + this.getDescription() + ", " + this.getPrice() + "euros"); 18 | } 19 | 20 | createIterator() { 21 | const arr = []; 22 | return arr[Symbol.iterator](); 23 | } 24 | } 25 | 26 | export default MenuItem; -------------------------------------------------------------------------------- /Chapter07/Composite Iterator/main.js: -------------------------------------------------------------------------------- 1 | import Menu from './Menu'; 2 | import MenuItem from './MenuItem'; 3 | import Mattress from './Mattress'; 4 | 5 | const oPanCakeHouseMenu = new Menu("Pancake House Menu", "Breakfast"); 6 | 7 | const oCoffeeMenu = new Menu("Cafe Menu", "Lunch"); 8 | oCoffeeMenu.add(new MenuItem("Express", "Coffee from machine", false, 0.99)); 9 | 10 | const oLunchMenu = new Menu("Lunch Menu", "Lunch"); 11 | oLunchMenu.add(new MenuItem("Pasta", 12 | "Spaghetti with Marinara Sauce, and a slice of sourdough bread", 13 | true, 14 | 3.89)); 15 | oLunchMenu.add(oCoffeeMenu); 16 | 17 | const oAllMenus = new Menu("ALL MENUS", "All menus combined"); 18 | 19 | oAllMenus.add(oPanCakeHouseMenu); 20 | oAllMenus.add(oLunchMenu); 21 | 22 | const oMattress = new Mattress(oAllMenus); 23 | oMattress.printMenu(); -------------------------------------------------------------------------------- /Chapter07/Interpreter/Context.js: -------------------------------------------------------------------------------- 1 | class Context { 2 | constructor(input) { 3 | this.input = input; 4 | this.output = ''; 5 | } 6 | } 7 | 8 | export default Context; -------------------------------------------------------------------------------- /Chapter07/Interpreter/DecimalToRomanInterpreter.js: -------------------------------------------------------------------------------- 1 | import Interpreter from './Interpreter'; 2 | 3 | class DecimalToRomanInterpreter extends Interpreter{ 4 | interpret(context) { 5 | let input = context.input; 6 | if(input <= 0) { 7 | return false; 8 | } else { 9 | const romanDecimals = { 10 | M:1000, 11 | CM:900, 12 | D:500, 13 | CD:400, 14 | C:100, 15 | XC:90, 16 | L:50, 17 | XL:40, 18 | X:10, 19 | IX:9, 20 | V:5, 21 | IV:4, 22 | I:1 23 | }; 24 | for(let key in romanDecimals) { 25 | while(input >= romanDecimals[key]) { 26 | context.output += key; 27 | input -= romanDecimals[key]; 28 | } 29 | } 30 | return true; 31 | } 32 | } 33 | } 34 | 35 | export default DecimalToRomanInterpreter; -------------------------------------------------------------------------------- /Chapter07/Interpreter/Interpreter.js: -------------------------------------------------------------------------------- 1 | class Interpreter { 2 | interpret() { 3 | throw new Error('This method should be overwritten'); 4 | } 5 | } 6 | 7 | export default Interpreter; -------------------------------------------------------------------------------- /Chapter07/Interpreter/main.js: -------------------------------------------------------------------------------- 1 | import Context from './Context'; 2 | import DecimalToRomanInterpreter from './DecimalToRomanInterpreter'; 3 | 4 | const context = new Context(1988); 5 | 6 | const d2RInterpreter = new DecimalToRomanInterpreter(); 7 | 8 | d2RInterpreter.interpret(context); 9 | 10 | console.log(context.output); -------------------------------------------------------------------------------- /Chapter07/Iterator/Iterator.js: -------------------------------------------------------------------------------- 1 | class Iterator { 2 | constructor(items) { 3 | if(typeof items[Symbol.iterator] !== 'function') { 4 | const keys = Object.keys(items); 5 | items[Symbol.iterator] = () => { 6 | return { 7 | next: () => { 8 | return { value: items[keys.shift()], done: keys.length === 0 }; 9 | } 10 | }; 11 | }; 12 | } 13 | return items[Symbol.iterator](); 14 | } 15 | } 16 | 17 | export default Iterator; -------------------------------------------------------------------------------- /Chapter07/Iterator/LunchMenu.js: -------------------------------------------------------------------------------- 1 | import Menu from './Menu'; 2 | import MenuItem from './MenuItem'; 3 | 4 | const MAX_ITEMS = 6; 5 | 6 | class LunchMenu extends Menu { 7 | constructor() { 8 | super(); 9 | this.addItem("Vegetarian BLT", "(Fakin') Bacon with lettuce and tomato on whole wheat", true, 2.99); 10 | this.addItem("BLT", "Bacon with lettuce and tomato on whole wheat", false, 2.99); 11 | this.addItem("Soup of the day", "Soup of the day, with a side of potato salad", false, 3.29); 12 | this.addItem("Hotdog", "A hotdog with saurkraut, relish, onions, topped with cheese", false, 3.05); 13 | } 14 | 15 | addItem(name, description, isVegetarian, price) { 16 | if (this.length === MAX_ITEMS) { 17 | throw new Error("Sorry menu is full! Can't add item to menu"); 18 | } 19 | super.addItem(new MenuItem({ 20 | name: name, 21 | description: description, 22 | isVegetarian: isVegetarian, 23 | price: price 24 | })); 25 | } 26 | } 27 | 28 | export default LunchMenu; -------------------------------------------------------------------------------- /Chapter07/Iterator/Mattress.js: -------------------------------------------------------------------------------- 1 | import LunchMenu from './LunchMenu'; 2 | import Iterator from './Iterator'; 3 | 4 | class Mattress { 5 | constructor() { 6 | this.lunchItems = new LunchMenu().getMenuItems(); 7 | } 8 | 9 | printMenu() { 10 | const iterator = new Iterator(this.lunchItems); 11 | let item = iterator.next(); 12 | let value; 13 | while (!item.done) { 14 | value = item.value; 15 | console.log(value.getName() + ': ' + value.getDescription() + ', ' + value.getPrice() + 'eur.'); 16 | item = iterator.next(); 17 | } 18 | } 19 | } 20 | 21 | export default Mattress; -------------------------------------------------------------------------------- /Chapter07/Iterator/Menu.js: -------------------------------------------------------------------------------- 1 | class Menu { 2 | constructor() { 3 | this.menuItems = []; 4 | this.length = 0; 5 | } 6 | 7 | addItem(menuItem) { 8 | this.menuItems.push(menuItem); 9 | this.length = this.menuItems.length; 10 | } 11 | 12 | getMenuItems() { 13 | return this.menuItems.concat([]); 14 | } 15 | } 16 | 17 | export default Menu; -------------------------------------------------------------------------------- /Chapter07/Iterator/MenuItem.js: -------------------------------------------------------------------------------- 1 | import Menu from "../Composite Iterator/Menu"; 2 | 3 | class MenuItem { 4 | constructor({ 5 | name = '', 6 | description = '', 7 | isVegetarian = false, 8 | price = 0 9 | }) { 10 | this.name = name; 11 | this.description = description; 12 | this._isVegetarian = isVegetarian; 13 | this.price = price; 14 | } 15 | 16 | getName() { 17 | return this.name; 18 | } 19 | 20 | getDescription() { 21 | return this.description; 22 | } 23 | 24 | getPrice() { 25 | return this.price; 26 | } 27 | 28 | isVegetarian() { 29 | return this._isVegetarian; 30 | } 31 | } 32 | 33 | export default MenuItem; -------------------------------------------------------------------------------- /Chapter07/Iterator/main.js: -------------------------------------------------------------------------------- 1 | import Mattress from './Mattress'; 2 | 3 | let oMattress = new Mattress(); 4 | oMattress.printMenu(); -------------------------------------------------------------------------------- /Chapter07/Mediator/Airplane.js: -------------------------------------------------------------------------------- 1 | class Airplane { 2 | constructor(id, airportControlTower) { 3 | this.id = id; 4 | this.airportControlTower = airportControlTower; 5 | this.pendingRequest = ''; 6 | } 7 | 8 | askLanding() { 9 | this.pendingRequest = 'land'; 10 | this.airportControlTower.notify(this, 'askLanding'); 11 | } 12 | 13 | askTakeOff() { 14 | this.pendingRequest = 'takeOff'; 15 | this.airportControlTower.notify(this, 'askTakeOff'); 16 | } 17 | 18 | wait(eventName) { 19 | setTimeout(() => { 20 | this[eventName](); 21 | }, 500); 22 | } 23 | 24 | land() { 25 | this.airportControlTower.notify(this, 'land'); 26 | } 27 | 28 | takeOff() { 29 | this.airportControlTower.notify(this, 'takeOff'); 30 | } 31 | } 32 | 33 | export default Airplane; -------------------------------------------------------------------------------- /Chapter07/Mediator/AirportControlTower.js: -------------------------------------------------------------------------------- 1 | import Mediator from './Mediator'; 2 | 3 | class AirportControlTower extends Mediator { 4 | constructor(lanes = [], flights = {}) { 5 | super(); 6 | this.lanes = lanes; 7 | this.flights = flights; 8 | } 9 | 10 | addLane(lane) { 11 | lane.mediator = this; 12 | this.lanes.push(lane); 13 | } 14 | 15 | getFreeLane() { 16 | let freeLane; 17 | this.lanes.some((lane) => { 18 | var isFree = lane.occupied === false; 19 | if (isFree) { 20 | freeLane = lane; 21 | } 22 | }); 23 | return freeLane; 24 | } 25 | 26 | notify(component, eventName) { 27 | if (eventName === 'laneIsOccupied') { 28 | console.log('Lane ' + component.number + ' is occupied by ' + component.airplane.id + '.'); 29 | component.airplane[component.airplane.pendingRequest](); 30 | component.setFree(); 31 | } else if (eventName === 'laneIsFree') { 32 | console.log('Lane ' + component.number + ' is free.'); 33 | } else if (eventName === 'askLanding' || eventName === 'askTakeOff') { 34 | if (!this.flights[component.id]) { 35 | this.flights[component.id] = component; 36 | } 37 | const freeLane = this.getFreeLane(); 38 | if (freeLane) { 39 | freeLane.setOccupied(component); 40 | } else { 41 | setTimeout(() => { 42 | this.notify(component, eventName); 43 | }, 500); 44 | } 45 | } else if (eventName === 'land') { 46 | console.log('Airplane ' + component.id + ' just landed'); 47 | } else if (eventName === 'takeOff') { 48 | console.log('Airplane ' + component.id + ' just took off'); 49 | } 50 | } 51 | } 52 | 53 | export default AirportControlTower; -------------------------------------------------------------------------------- /Chapter07/Mediator/AirportLane.js: -------------------------------------------------------------------------------- 1 | class AirportLane { 2 | constructor(number) { 3 | this.number = number; 4 | this.mediator = null; 5 | this.occupied = false; 6 | this.airplane = null; 7 | } 8 | 9 | addMediator(mediator) { 10 | this.mediator = mediator; 11 | } 12 | 13 | setOccupied(airplane) { 14 | this.airplane = airplane; 15 | this.occupied = true; 16 | this.mediator.notify(this, 'laneIsOccupied'); 17 | } 18 | 19 | setFree() { 20 | this.airplane = null; 21 | this.occupied = false; 22 | this.mediator.notify(this, 'laneIsFree'); 23 | } 24 | } 25 | 26 | export default AirportLane; -------------------------------------------------------------------------------- /Chapter07/Mediator/Mediator.js: -------------------------------------------------------------------------------- 1 | class Mediator { 2 | notify(component, eventName) { 3 | throw new Error('This method should be overwritten'); 4 | } 5 | } 6 | 7 | export default Mediator; -------------------------------------------------------------------------------- /Chapter07/Mediator/main.js: -------------------------------------------------------------------------------- 1 | import AirportControlTower from './AirportControlTower'; 2 | import AirportLane from './AirportLane'; 3 | import Airplane from './Airplane'; 4 | 5 | const AirportJFKTowerControl = new AirportControlTower(); 6 | 7 | AirportJFKTowerControl.addLane(new AirportLane(1)); 8 | AirportJFKTowerControl.addLane(new AirportLane(2)); 9 | 10 | const Airplane100BJC = new Airplane('100BJC', AirportJFKTowerControl); 11 | const Airplane365MPD = new Airplane('365MPD', AirportJFKTowerControl); 12 | 13 | Airplane100BJC.askLanding(); 14 | 15 | Airplane365MPD.askTakeOff(); -------------------------------------------------------------------------------- /Chapter07/Memento/Caretaker.js: -------------------------------------------------------------------------------- 1 | const privateHistory = new WeakMap(); 2 | class Caretaker { 3 | constructor() { 4 | privateHistory.set(this, []); 5 | } 6 | get history() { 7 | return privateHistory.get(this); 8 | } 9 | addToHistory(snapshot) { 10 | const history = this.history; 11 | history.push(snapshot); 12 | privateHistory.set(this, history); 13 | } 14 | undo() { 15 | const snapshot = this.history.pop(); 16 | if (snapshot) { 17 | console.log(snapshot.originator.state); 18 | snapshot.restore(); 19 | } 20 | } 21 | } 22 | 23 | export default Caretaker; -------------------------------------------------------------------------------- /Chapter07/Memento/Command.js: -------------------------------------------------------------------------------- 1 | import Originator from './Originator'; 2 | 3 | const privateStateCommand = new WeakMap(); 4 | class Command extends Originator{ 5 | constructor(state) { 6 | super(); 7 | privateStateCommand.set(this, state); 8 | } 9 | get state() { 10 | return privateStateCommand.get(this); 11 | } 12 | set state(state) { 13 | privateStateCommand.set(this, state); 14 | } 15 | save() { 16 | return new Snapshot(this, this.state); 17 | } 18 | } 19 | 20 | export default Command; -------------------------------------------------------------------------------- /Chapter07/Memento/Line.js: -------------------------------------------------------------------------------- 1 | import Command from './Command'; 2 | 3 | class Line extends Command { 4 | constructor(startX=0, startY=0, endX=0, endY=0) { 5 | super({ 6 | startX, 7 | startY, 8 | endX, 9 | endY 10 | }); 11 | } 12 | setStartPoint(x, y) { 13 | const state = this.state; 14 | state.startX = x; 15 | state.startY = y; 16 | this.state = state; 17 | } 18 | setEndPoint(x, y) { 19 | const state = this.state; 20 | state.endX = x; 21 | state.endY = y; 22 | this.state = state; 23 | } 24 | } 25 | 26 | export default Line; -------------------------------------------------------------------------------- /Chapter07/Memento/LineEditor.js: -------------------------------------------------------------------------------- 1 | import Caretaker from './Caretaker'; 2 | 3 | class LineEditor extends Caretaker { 4 | constructor() { 5 | super(); 6 | this.line = null; 7 | } 8 | onMouseDown(event) { 9 | this.line = new Line(); 10 | this.line.setStartPoint(event.clientX, event.clientY); 11 | } 12 | onMouseUp(event) { 13 | this.line.setEndPoint(event.clientX, event.clientY); 14 | this.addToHistory(this.line.save()); 15 | } 16 | } 17 | 18 | export default LineEditor; -------------------------------------------------------------------------------- /Chapter07/Memento/Memento.js: -------------------------------------------------------------------------------- 1 | class Memento { 2 | restore() { 3 | throw new Error('This method should be overwritten'); 4 | } 5 | } 6 | 7 | export default Memento; -------------------------------------------------------------------------------- /Chapter07/Memento/Originator.js: -------------------------------------------------------------------------------- 1 | class Originator { 2 | save() { 3 | throw new Error('This method should be overwritten'); 4 | } 5 | } 6 | 7 | export default Originator; -------------------------------------------------------------------------------- /Chapter07/Memento/Painter.js: -------------------------------------------------------------------------------- 1 | import Caretaker from './Caretaker'; 2 | 3 | class Painter extends Caretaker{ 4 | constructor(commands) { 5 | super(); 6 | this.commands = commands; 7 | } 8 | } 9 | 10 | export default Painter; -------------------------------------------------------------------------------- /Chapter07/Memento/Snapshot.js: -------------------------------------------------------------------------------- 1 | import Memento from './Memento'; 2 | 3 | const privateStateSnapshot = new WeakMap(); 4 | class Snapshot extends Memento{ 5 | constructor(originator, state) { 6 | super(); 7 | this.originator = originator; 8 | privateStateSnapshot.set(this, state); 9 | } 10 | get state() { 11 | return privateStateSnapshot.get(this) 12 | } 13 | restore() { 14 | this.originator.state = this.state; 15 | } 16 | } 17 | 18 | export default Snapshot; -------------------------------------------------------------------------------- /Chapter07/Memento/main.js: -------------------------------------------------------------------------------- 1 | const painter = new Painter({ line: new LineEditor() }); 2 | 3 | // Draw a square 4 | painter.commands.line.onMouseDown({ clientX: 0, clientY: 0}); 5 | painter.commands.line.onMouseUp({ clientX: 200, clientY: 0}); 6 | 7 | painter.commands.line.onMouseDown({ clientX: 200, clientY: 0}); 8 | painter.commands.line.onMouseUp({ clientX: 200, clientY: 200}); 9 | 10 | painter.commands.line.onMouseDown({ clientX: 200, clientY: 200}); 11 | painter.commands.line.onMouseUp({ clientX: 0, clientY: 200}); 12 | 13 | painter.commands.line.onMouseDown({ clientX: 0, clientY: 200}); 14 | painter.commands.line.onMouseUp({ clientX: 0, clientY: 0}); 15 | 16 | // Undo the square 17 | painter.commands.line.undo(); 18 | painter.commands.line.undo(); 19 | painter.commands.line.undo(); 20 | painter.commands.line.undo(); -------------------------------------------------------------------------------- /Chapter07/Observer/CurrentConditionsDisplay.js: -------------------------------------------------------------------------------- 1 | import Observable from './Observable'; 2 | import Displayable from './Displayable'; 3 | 4 | class CurrentConditionsDisplay extends Observable(Displayable()) { 5 | constructor(subject) { 6 | super(); 7 | this.temperature = 0; 8 | this.humidity = 0; 9 | this.pressure = 0; 10 | this.subject = subject; 11 | this.subject.registerObserver(this); 12 | } 13 | update(temperature, humidity, pressure) { 14 | this.temperature = temperature; 15 | this.humidity = humidity; 16 | this.pressure = pressure; 17 | this.display(); 18 | } 19 | display() { 20 | console.log('Current conditions: ' + this.temperature + 'F degrees and ' + this.humidity + '% humidity.'); 21 | } 22 | } 23 | 24 | export default CurrentConditionsDisplay; -------------------------------------------------------------------------------- /Chapter07/Observer/Displayable.js: -------------------------------------------------------------------------------- 1 | const Displayable = (Sup=Object.constructor) => class extends Sup { 2 | display() { 3 | throw new Error('This method must be overwritten'); 4 | } 5 | }; 6 | 7 | export default Displayable; -------------------------------------------------------------------------------- /Chapter07/Observer/Observable.js: -------------------------------------------------------------------------------- 1 | const Observable = (Sup=Object.constructor) => class extends Sup { 2 | update() { 3 | throw new Error('This method must be overwritten'); 4 | } 5 | }; 6 | 7 | export default Observable; -------------------------------------------------------------------------------- /Chapter07/Observer/Subject.js: -------------------------------------------------------------------------------- 1 | class Subject { 2 | registerObserver() { 3 | throw new Error('This method must be overwritten'); 4 | } 5 | removeObserver() { 6 | throw new Error('This method must be overwritten'); 7 | } 8 | notifyObservers() { 9 | throw new Error('This method must be overwritten'); 10 | } 11 | } 12 | 13 | export default Subject; -------------------------------------------------------------------------------- /Chapter07/Observer/WeatherStation.js: -------------------------------------------------------------------------------- 1 | import Subject from './Subject'; 2 | 3 | class WeatherStation extends Subject { 4 | constructor() { 5 | super(); 6 | this.observers = []; 7 | this.temperature = 0; 8 | this.humidity = 0; 9 | this.pressure = 0; 10 | } 11 | registerObserver(observer) { 12 | this.observers[observer.id] = observer; 13 | } 14 | removeObserver(observer) { 15 | delete this.observers[observer.id]; 16 | } 17 | notifyObservers() { 18 | for(let observerId in this.observers) { 19 | if(this.observers.hasOwnProperty(observerId)) { 20 | this.observers[observerId].update(this.temperature, this.humidity, this.pressure); 21 | } 22 | } 23 | } 24 | measurementsChanged() { 25 | this.notifyObservers(); 26 | } 27 | setMeasurements(temperature, humidity, pressure) { 28 | this.temperature = temperature; 29 | this.humidity = humidity; 30 | this.pressure = pressure; 31 | this.measurementsChanged(); 32 | } 33 | } 34 | 35 | export default WeatherStation; -------------------------------------------------------------------------------- /Chapter07/Observer/main.js: -------------------------------------------------------------------------------- 1 | const oWeatherStation = new WeatherStation(); 2 | const oDisplay = new CurrentConditionsDisplay(oWeatherStation); 3 | 4 | oWeatherStation.setMeasurements(80, 65, 30.4); -------------------------------------------------------------------------------- /Chapter07/State/Download.js: -------------------------------------------------------------------------------- 1 | import ReadyState from './ReadyState'; 2 | import DownloadingState from './DownloadingState'; 3 | import DownloadPausedState from './DownloadPausedState'; 4 | import DownloadedState from './DownloadedState'; 5 | import DownloadFailedState from './DownloadFailedState'; 6 | 7 | class Download { 8 | constructor() { 9 | this.state = new ReadyState(this); 10 | } 11 | setState(state) { 12 | this.state = state; 13 | } 14 | download() { 15 | this.state.download(); 16 | } 17 | pause() { 18 | this.state.pause(); 19 | } 20 | fail() { 21 | this.state.fail(); 22 | } 23 | finish() { 24 | this.state.finish(); 25 | } 26 | getReadyState() { 27 | return new ReadyState(this); 28 | } 29 | getDownloadingState() { 30 | return new DownloadingState(this); 31 | } 32 | getDownloadPausedState() { 33 | return new DownloadPausedState(this); 34 | } 35 | getDownloadedState() { 36 | return new DownloadedState(this); 37 | } 38 | getDownloadedFailedState() { 39 | return new DownloadFailedState(this); 40 | } 41 | } 42 | 43 | export default Download; -------------------------------------------------------------------------------- /Chapter07/State/DownloadFailedState.js: -------------------------------------------------------------------------------- 1 | import State from './State'; 2 | 3 | const privateDownload = new WeakMap(); 4 | 5 | class DownloadFailedState extends State { 6 | constructor(download) { 7 | super(); 8 | privateDownload.set(this, download); 9 | } 10 | get _download() { 11 | return privateDownload.get(this); 12 | } 13 | download() { 14 | this._download.setState(this._download.getDownloadingState()); 15 | console.log('Try to Download again!'); 16 | } 17 | pause() { 18 | throw new Error('You cannot pause a download if it failed!'); 19 | } 20 | fail() { 21 | throw new Error('A failed download cannot fail because it already failed!'); 22 | } 23 | finish() { 24 | throw new Error('A failed download cannot be finished!'); 25 | } 26 | } 27 | 28 | export default DownloadFailedState; -------------------------------------------------------------------------------- /Chapter07/State/DownloadPausedState.js: -------------------------------------------------------------------------------- 1 | import State from './State'; 2 | 3 | const privateDownload = new WeakMap(); 4 | 5 | class DownloadPausedState extends State { 6 | constructor(download) { 7 | super(); 8 | privateDownload.set(this, download); 9 | } 10 | get _download() { 11 | return privateDownload.get(this); 12 | } 13 | download() { 14 | this._download.setState(this._download.getDownloadingState()); 15 | console.log('Resume Download!'); 16 | } 17 | pause() { 18 | throw new Error('You cannot pause a download that is already paused!'); 19 | } 20 | fail() { 21 | this._download.setState(this._download.getDownloadedFailedState()); 22 | console.log('Download has failed!'); 23 | } 24 | finish() { 25 | this._download.setState(this._download.getDownloadedState()); 26 | console.log('Download has finished!'); 27 | } 28 | } 29 | 30 | export default DownloadPausedState; -------------------------------------------------------------------------------- /Chapter07/State/DownloadedState.js: -------------------------------------------------------------------------------- 1 | import State from './State'; 2 | 3 | const privateDownload = new WeakMap(); 4 | 5 | class DownloadedState extends State { 6 | constructor(download) { 7 | super(); 8 | privateDownload.set(this, download); 9 | } 10 | get _download() { 11 | return privateDownload.get(this); 12 | } 13 | download() { 14 | this._download.setState(this._download.getDownloadingState()); 15 | console.log('Download again!'); 16 | } 17 | pause() { 18 | throw new Error('You cannot pause a file if it is not being downloaded!'); 19 | } 20 | fail() { 21 | throw new Error('A downloaded file cannot fail!'); 22 | } 23 | finish() { 24 | throw new Error('A downloaded file cannot finish because it is already finished!'); 25 | } 26 | } 27 | 28 | export default DownloadedState; -------------------------------------------------------------------------------- /Chapter07/State/DownloadingState.js: -------------------------------------------------------------------------------- 1 | import State from './State'; 2 | 3 | const privateDownload = new WeakMap(); 4 | 5 | class DownloadingState extends State { 6 | constructor(download) { 7 | super(); 8 | privateDownload.set(this, download); 9 | } 10 | get _download() { 11 | return privateDownload.get(this); 12 | } 13 | download() { 14 | throw new Error('You cannot download a file while it is being downloaded!'); 15 | } 16 | pause() { 17 | this._download.setState(this._download.getDownloadPausedState()); 18 | console.log('Download has been paused!'); 19 | } 20 | fail() { 21 | this._download.setState(this._download.getDownloadedFailedState()); 22 | console.log('Download has failed!'); 23 | } 24 | finish() { 25 | this._download.setState(this._download.getDownloadedState()); 26 | console.log('Download has finished!'); 27 | } 28 | } 29 | 30 | export default DownloadingState; -------------------------------------------------------------------------------- /Chapter07/State/ReadyState.js: -------------------------------------------------------------------------------- 1 | import State from './State'; 2 | 3 | const privateDownload = new WeakMap(); 4 | 5 | class ReadyState extends State { 6 | constructor(download) { 7 | super(); 8 | privateDownload.set(this, download); 9 | } 10 | get _download() { 11 | return privateDownload.get(this); 12 | } 13 | download() { 14 | this._download.setState(this._download.getDownloadingState()); 15 | console.log('Start Download!'); 16 | } 17 | pause() { 18 | throw new Error('You cannot pause a download when it has not started yet!'); 19 | } 20 | fail() { 21 | throw new Error('A download cannot fail if it has not started!'); 22 | } 23 | finish() { 24 | throw new Error('A download cannot finish if it has not started!'); 25 | } 26 | } 27 | 28 | export default ReadyState; -------------------------------------------------------------------------------- /Chapter07/State/State.js: -------------------------------------------------------------------------------- 1 | class State { 2 | download() { 3 | throw new Error('This method must be overwritten!'); 4 | } 5 | pause() { 6 | throw new Error('This method must be overwritten!'); 7 | } 8 | fail() { 9 | throw new Error('This method must be overwritten!'); 10 | } 11 | finish() { 12 | throw new Error('This method must be overwritten!'); 13 | } 14 | } 15 | 16 | export default State; -------------------------------------------------------------------------------- /Chapter07/State/main.js: -------------------------------------------------------------------------------- 1 | import Download from './Download'; 2 | 3 | const oDownload = new Download(); 4 | 5 | // Happy and regular flow 6 | oDownload.download(); 7 | oDownload.pause(); 8 | oDownload.download(); 9 | oDownload.finish(); 10 | 11 | 12 | // Unhappy flows after finishing the download. 13 | oDownload.pause(); 14 | oDownload.finish(); -------------------------------------------------------------------------------- /Chapter07/Strategy/DecoyDuck.js: -------------------------------------------------------------------------------- 1 | import Duck from './Duck'; 2 | 3 | class DecoyDuck extends Duck { 4 | display() { 5 | console.log("DecoyDuck show"); 6 | } 7 | } 8 | 9 | export default DecoyDuck; -------------------------------------------------------------------------------- /Chapter07/Strategy/Duck.js: -------------------------------------------------------------------------------- 1 | class Duck { 2 | swim() { 3 | console.log('Chop!'); 4 | } 5 | display() { 6 | throw new Error("This method must be overwritten!"); 7 | } 8 | } 9 | 10 | export default Duck; -------------------------------------------------------------------------------- /Chapter07/Strategy/Flyable.js: -------------------------------------------------------------------------------- 1 | const Flyable = (Sup = Object.constructor) => class extends Sup { 2 | fly() { 3 | console.log('Wings!'); 4 | } 5 | }; 6 | 7 | export default Flyable; -------------------------------------------------------------------------------- /Chapter07/Strategy/MallardDuck.js: -------------------------------------------------------------------------------- 1 | import Quackable from './Quackable'; 2 | import Flyable from './Flyable'; 3 | 4 | class MallardDuck extends Quackable(Flyable(Duck)) { 5 | display() { 6 | console.log('MallardDuck show'); 7 | } 8 | fly() { 9 | console.log('Fly 100 miles'); 10 | } 11 | } 12 | 13 | export default MallardDuck; -------------------------------------------------------------------------------- /Chapter07/Strategy/Quackable.js: -------------------------------------------------------------------------------- 1 | const Quackable = (Sup = Object.constructor) => class extends Sup { 2 | quack() { 3 | console.log('Quack!'); 4 | } 5 | }; 6 | 7 | export default Quackable; -------------------------------------------------------------------------------- /Chapter07/Strategy/RedheadDuck.js: -------------------------------------------------------------------------------- 1 | import Quackable from './Quackable'; 2 | import Flyable from './Flyable'; 3 | import Duck from './Duck'; 4 | 5 | class RedheadDuck extends Quackable(Flyable(Duck)) { 6 | display() { 7 | console.log('RedheadDuck show'); 8 | } 9 | fly() { 10 | console.log('Fly 30 miles'); 11 | } 12 | } 13 | 14 | export default RedheadDuck; -------------------------------------------------------------------------------- /Chapter07/Strategy/RubberDuck.js: -------------------------------------------------------------------------------- 1 | import Quackable from './Quackable'; 2 | import Duck from './Duck'; 3 | 4 | class RubberDuck extends Quackable(Duck) { 5 | display() { 6 | console.log('RubberDuck show') 7 | } 8 | quack() { 9 | console.log('Iiiiaaa!'); 10 | } 11 | } 12 | 13 | export default RubberDuck; -------------------------------------------------------------------------------- /Chapter07/Strategy/main.js: -------------------------------------------------------------------------------- 1 | import MallardDuck from './MallardDuck'; 2 | import RedheadDuck from './RedheadDuck'; 3 | import RubberDuck from './RubberDuck'; 4 | import DecoyDuck from './DecoyDuck'; 5 | 6 | const mallard = new MallardDuck(); 7 | const redhead = new RedheadDuck(); 8 | const rubber = new RubberDuck(); 9 | const decoy = new DecoyDuck(); 10 | 11 | mallard.quack(); 12 | mallard.swim(); 13 | mallard.fly(); 14 | mallard.display(); 15 | 16 | redhead.quack(); 17 | redhead.swim(); 18 | redhead.fly(); 19 | redhead.display(); 20 | 21 | rubber.quack(); 22 | rubber.swim(); 23 | rubber.display(); 24 | 25 | decoy.display(); -------------------------------------------------------------------------------- /Chapter07/Template Method/CaffeineBeverage.js: -------------------------------------------------------------------------------- 1 | class CaffeineBeverage { 2 | prepareRecipe() { 3 | this.boilWater(); 4 | this.brew(); 5 | this.pourOnCup(); 6 | this.customerWantsCondiments() 7 | .then((wantsCondiments) => { 8 | if (wantsCondiments) { 9 | this.addCondiments(); 10 | } 11 | }); 12 | } 13 | 14 | boilWater() { 15 | console.log("Put water on fire until the water starts boiling!"); 16 | } 17 | 18 | pourOnCup() { 19 | console.log("Put beverage on Cup!"); 20 | } 21 | 22 | brew() { 23 | throw new Error("This method mus be overwritten!"); 24 | } 25 | 26 | addCondiments() { 27 | throw new Error("This method mus be overwritten!"); 28 | } 29 | 30 | customerWantsCondiments() { 31 | return new Promise((resolve, reject) => { 32 | resolve(true); 33 | }); 34 | } 35 | } 36 | 37 | export default CaffeineBeverage; -------------------------------------------------------------------------------- /Chapter07/Template Method/Coffee.js: -------------------------------------------------------------------------------- 1 | import Confirm from 'prompt-confirm'; 2 | import CaffeineBeverage from './CaffeineBeverage'; 3 | 4 | class Coffee extends CaffeineBeverage { 5 | brew() { 6 | console.log("Dripping Coffee through filter!"); 7 | } 8 | addCondiments() { 9 | console.log("Add Sugar and Milk!"); 10 | } 11 | customerWantsCondiments() { 12 | const prompt = new Confirm('Do you want sugar and milk?'); 13 | return prompt.run(); 14 | } 15 | } 16 | 17 | export default Coffee; -------------------------------------------------------------------------------- /Chapter07/Template Method/Tea.js: -------------------------------------------------------------------------------- 1 | import Confirm from 'prompt-confirm'; 2 | import CaffeineBeverage from './CaffeineBeverage'; 3 | 4 | class Tea extends CaffeineBeverage { 5 | brew() { 6 | console.log("Steeping the tea!"); 7 | } 8 | addCondiments() { 9 | console.log("Adding lemon!"); 10 | } 11 | customerWantsCondiments() { 12 | const prompt = new Confirm('Do you want some lemon?'); 13 | return prompt.run(); 14 | } 15 | } 16 | 17 | export default Tea; -------------------------------------------------------------------------------- /Chapter07/Template Method/main.js: -------------------------------------------------------------------------------- 1 | import Coffee from './Coffee'; 2 | 3 | const oCoffee = new Coffee(); 4 | oCoffee.prepareRecipe(); 5 | 6 | console.log("*********************************************************"); 7 | 8 | const oTea = new Tea(); 9 | oTea.prepareRecipe(); -------------------------------------------------------------------------------- /Chapter07/Visitor/Book.js: -------------------------------------------------------------------------------- 1 | import Visitable from './Visitable'; 2 | 3 | const privateBookPrice= new WeakMap(); 4 | const privateBookWeight= new WeakMap(); 5 | 6 | class Book extends Visitable { 7 | constructor(price=0, weight=0) { 8 | super(); 9 | privateBookPrice.set(this, price); 10 | privateBookWeight.set(this, weight); 11 | } 12 | get price() { 13 | return privateBookPrice.get(this); 14 | } 15 | get weight() { 16 | return privateBookWeight.get(this); 17 | } 18 | accept(visitor) { 19 | visitor.visit(this); 20 | } 21 | } 22 | 23 | export default Book; -------------------------------------------------------------------------------- /Chapter07/Visitor/PostageVisitor.js: -------------------------------------------------------------------------------- 1 | import Visitor from './Visitor'; 2 | 3 | class PostageVisitor extends Visitor { 4 | constructor(totalPostageForCart=0) { 5 | super(); 6 | this.totalPostageForCart = totalPostageForCart; 7 | } 8 | visit(item) { 9 | if(item.price <= 10) { 10 | this.totalPostageForCart += item.weight * 2; 11 | } 12 | } 13 | get totalPostage() { 14 | return this.totalPostageForCart; 15 | } 16 | } 17 | 18 | export default PostageVisitor; -------------------------------------------------------------------------------- /Chapter07/Visitor/ShoppingCart.js: -------------------------------------------------------------------------------- 1 | import PostageVisitor from './PostageVisitor'; 2 | 3 | const privateCartItems = new WeakMap(); 4 | 5 | class ShoppingCart { 6 | constructor() { 7 | privateCartItems.set(this, []); 8 | } 9 | get items() { 10 | return privateCartItems.get(this); 11 | } 12 | addItem(item) { 13 | const items = this.items; 14 | items.push(item); 15 | privateCartItems.set(this, items); 16 | } 17 | calculateTotalPrice() { 18 | const amountCart = this.calculateAmountCart(); 19 | const amountPostage = this.calculatePostage(); 20 | return amountCart + amountPostage; 21 | } 22 | calculateAmountCart() { 23 | return this.items.reduce((reducer, item) => { 24 | return (reducer += item.price); 25 | }, 0); 26 | } 27 | calculatePostage() { 28 | const visitor = new PostageVisitor(); 29 | this.items.forEach((item) => { 30 | item.accept(visitor); 31 | }); 32 | return visitor.totalPostage; 33 | } 34 | } 35 | 36 | export default ShoppingCart; -------------------------------------------------------------------------------- /Chapter07/Visitor/Visitable.js: -------------------------------------------------------------------------------- 1 | class Visitable { 2 | accept(visitor) { 3 | throw new Error('This method should be overwritten'); 4 | } 5 | } 6 | 7 | export default Visitable; -------------------------------------------------------------------------------- /Chapter07/Visitor/Visitor.js: -------------------------------------------------------------------------------- 1 | class Visitor { 2 | visit(item) { 3 | throw new Error('This method should be overwritten'); 4 | } 5 | } 6 | 7 | export default Visitor; -------------------------------------------------------------------------------- /Chapter07/Visitor/main.js: -------------------------------------------------------------------------------- 1 | import Book from './Book'; 2 | 3 | const oShoppingCart = new ShoppingCart(); 4 | 5 | oShoppingCart.addItem(new Book(7.95, 0.400)); 6 | oShoppingCart.addItem(new Book(3.75,0.160 )); 7 | 8 | console.log('Amount to be paid for all the items in the cart: ' + oShoppingCart.calculateAmountCart()); 9 | console.log('Amount to be paid for shipping costs: ' + oShoppingCart.calculatePostage()); 10 | console.log('Amount to be paid for all the items in the cart plus shipping costs:' + oShoppingCart.calculateTotalPrice()); 11 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Packt 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Mastering-JavaScript-Design-Patterns-Third-Edition 2 | Mastering-JavaScript-Design-Patterns-Third-Edition 3 | # The code for this repository is under development :construction_worker: 4 | --------------------------------------------------------------------------------