├── 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 |
--------------------------------------------------------------------------------