diff --git a/Node.js-design-patterns/Behaviorals/Command/commands.js b/Node.js-design-patterns/Behaviorals/Command/commands.js new file mode 100644 index 0000000..0fa9eec --- /dev/null +++ b/Node.js-design-patterns/Behaviorals/Command/commands.js @@ -0,0 +1,34 @@ +var { writeFile } = require('fs'); +var path = require('path'); + +class ExitCommand { + + get name() { + return 'exit... bye!'; + } + + execute() { + process.exit(0); + } + +} + +class CreateCommand { + + constructor(fileName, text) { + this.fileName = fileName; + this.body = text; + this.fullPath = path.join(__dirname, fileName); + } + + get name() { + return `create ${this.fileName}`; + } + + execute() { + writeFile(this.fullPath, this.body, f => f); + } + +} + +module.exports = { ExitCommand, CreateCommand }; diff --git a/Node.js-design-patterns/Behaviorals/Command/conductor.js b/Node.js-design-patterns/Behaviorals/Command/conductor.js new file mode 100644 index 0000000..5f6b076 --- /dev/null +++ b/Node.js-design-patterns/Behaviorals/Command/conductor.js @@ -0,0 +1,10 @@ +class Conductor { + + run(command) { + console.log(`Executing command: ${command.name}`); + command.execute(); + } + +} + +module.exports = new Conductor(); diff --git a/Node.js-design-patterns/Behaviorals/Command/index.js b/Node.js-design-patterns/Behaviorals/Command/index.js new file mode 100644 index 0000000..3cf8232 --- /dev/null +++ b/Node.js-design-patterns/Behaviorals/Command/index.js @@ -0,0 +1,35 @@ +var conductor = require('./conductor'); +var { ExitCommand, CreateCommand } = require('./commands'); + +var { createInterface } = require('readline'); +var rl = createInterface({ + input: process.stdin, + output: process.stdout +}); + +console.log('create | exit'); +rl.prompt(); + +rl.on('line', input => { + + var [ commandText, ...remaining ] = input.split(' ') + var [ fileName, ...fileText ] = remaining + var text = fileText.join(' ') + + switch(commandText) { + + case "exit": + conductor.run(new ExitCommand()); + break; + + case "create" : + conductor.run(new CreateCommand(fileName, text)); + break; + + default : + console.log(`${commandText} command not found!`); + } + + rl.prompt(); + +}); diff --git a/Node.js-design-patterns/Behaviorals/Iterator/InventoryItem.js b/Node.js-design-patterns/Behaviorals/Iterator/InventoryItem.js new file mode 100644 index 0000000..9c39e19 --- /dev/null +++ b/Node.js-design-patterns/Behaviorals/Iterator/InventoryItem.js @@ -0,0 +1,14 @@ +class InventoryItem { + + constructor(name, price) { + this.name = name; + this.price = price; + } + + writeLn() { + process.stdout.write(`${this.name}: $${this.price}`); + } + +} + +module.exports = InventoryItem; diff --git a/Node.js-design-patterns/Behaviorals/Iterator/Iterator.js b/Node.js-design-patterns/Behaviorals/Iterator/Iterator.js new file mode 100644 index 0000000..2a77d0f --- /dev/null +++ b/Node.js-design-patterns/Behaviorals/Iterator/Iterator.js @@ -0,0 +1,42 @@ +class Iterator { + + constructor(items=[]) { + this.index = 0; + this.items = items; + } + + first() { + var [first] = this.items; + return first; + } + + last() { + var [last] = [...this.items].reverse(); + return last; + } + + hasNext() { + return this.index < this.items.length - 1; + } + + current() { + return this.items[this.index]; + } + + next() { + if (this.hasNext()) { + this.index += 1; + } + return this.current(); + } + + prev() { + if (this.index !== 0) { + this.index -= 1; + } + return this.current(); + } + +} + +module.exports = Iterator; diff --git a/Node.js-design-patterns/Behaviorals/Iterator/index.js b/Node.js-design-patterns/Behaviorals/Iterator/index.js new file mode 100644 index 0000000..ebd34e6 --- /dev/null +++ b/Node.js-design-patterns/Behaviorals/Iterator/index.js @@ -0,0 +1,49 @@ +var InventoryItem = require('./InventoryItem'); +var Iterator = require('./Iterator'); + +require('readline').emitKeypressEvents(process.stdin); +process.stdin.setRawMode(true); + +console.log('Press any direction key...'); + +var inventory = new Iterator([ + new InventoryItem("Poles", 9.99), + new InventoryItem("Skis", 799.99), + new InventoryItem("Boots", 799.99), + new InventoryItem("Burgers", 5.99), + new InventoryItem("Fries", 2.99), + new InventoryItem("Shake", 4.99), + new InventoryItem("Jeans", 59.99), + new InventoryItem("Shoes", 39.99) +]); + +process.stdin.on('keypress', (str, key) => { + + process.stdout.clearLine(); + process.stdout.cursorTo(0); + + switch(key.name) { + + case 'right' : + inventory.next().writeLn(); + break; + + case 'left' : + inventory.prev().writeLn(); + break; + + case 'down' : + inventory.last().writeLn(); + break; + + case 'up' : + inventory.first().writeLn(); + break; + + case 'c' : + if (key.ctrl) { + process.exit() + } + } + +}); diff --git a/Node.js-design-patterns/Behaviorals/Observer/Mall.js b/Node.js-design-patterns/Behaviorals/Observer/Mall.js new file mode 100644 index 0000000..01bccf6 --- /dev/null +++ b/Node.js-design-patterns/Behaviorals/Observer/Mall.js @@ -0,0 +1,13 @@ +class Mall { + + constructor() { + this.sales = []; + } + + notify(storeName, discount) { + this.sales.push({ storeName, discount }); + } + +} + +module.exports = Mall; diff --git a/Node.js-design-patterns/Behaviorals/Observer/Shopper.js b/Node.js-design-patterns/Behaviorals/Observer/Shopper.js new file mode 100644 index 0000000..21a0548 --- /dev/null +++ b/Node.js-design-patterns/Behaviorals/Observer/Shopper.js @@ -0,0 +1,12 @@ +class Shopper { + + constructor(name) { + this.name = name; + } + + notify(storeName, discount) { + console.log(`${this.name}, there is a sale at ${storeName}! ${discount}% off everything!`); + } +} + +module.exports = Shopper; diff --git a/Node.js-design-patterns/Behaviorals/Observer/Store.js b/Node.js-design-patterns/Behaviorals/Observer/Store.js new file mode 100644 index 0000000..a7da40c --- /dev/null +++ b/Node.js-design-patterns/Behaviorals/Observer/Store.js @@ -0,0 +1,18 @@ +class Store { + + constructor(name) { + this.name = name; + this.subscribers = []; + } + + subscribe(observer) { + this.subscribers.push(observer); + } + + sale(discount) { + this.subscribers.forEach(observer => observer.notify(this.name, discount)); + } + +} + +module.exports = Store; diff --git a/Node.js-design-patterns/Behaviorals/Observer/index.js b/Node.js-design-patterns/Behaviorals/Observer/index.js new file mode 100644 index 0000000..fd38642 --- /dev/null +++ b/Node.js-design-patterns/Behaviorals/Observer/index.js @@ -0,0 +1,26 @@ +var Store = require('./Store'); +var Shopper = require('./Shopper'); +var Mall = require('./Mall'); + +var catsAndThings = new Store("Cats & Things"); +var insAndOuts = new Store("Ins and Outs"); + +var alex = new Shopper("Alex"); +var eve = new Shopper("Eve"); +var sharon = new Shopper("Sharon"); +var mike = new Shopper("Mike"); + +var valleyMall = new Mall(); + +catsAndThings.subscribe(alex); +catsAndThings.subscribe(eve); +catsAndThings.subscribe(mike); +catsAndThings.subscribe(valleyMall); + +insAndOuts.subscribe(sharon); +insAndOuts.subscribe(valleyMall); + +catsAndThings.sale(20); +insAndOuts.sale(50); + +console.log( valleyMall.sales ); diff --git a/Node.js-design-patterns/Behaviorals/Strategy/LogStrategy.js b/Node.js-design-patterns/Behaviorals/Strategy/LogStrategy.js new file mode 100644 index 0000000..f1d7303 --- /dev/null +++ b/Node.js-design-patterns/Behaviorals/Strategy/LogStrategy.js @@ -0,0 +1,36 @@ +var path = require('path'); +var { appendFile } = require('fs'); +var morse = require('morse'); + +class LogStrategy { + + static toMorseCode(timestamp, message) { + var morseCode = morse.encode(message); + console.log(morseCode); + } + + static noDate(timestamp, message) { + console.log(message); + } + + static toFile(timestamp, message) { + var fileName = path.join(__dirname, 'logs.txt'); + appendFile(fileName, `${timestamp} - ${message} \n`, error => { + if (error) { + console.log('Error writing to file'); + console.error(error); + } + }) + } + + static toConsole(timestamp, message) { + console.log(`${timestamp} - ${message}`); + } + + static none() { + + } + +} + +module.exports = LogStrategy; diff --git a/Node.js-design-patterns/Behaviorals/Strategy/Logger.js b/Node.js-design-patterns/Behaviorals/Strategy/Logger.js new file mode 100644 index 0000000..9adebb9 --- /dev/null +++ b/Node.js-design-patterns/Behaviorals/Strategy/Logger.js @@ -0,0 +1,27 @@ +var LogStrategy = require('./LogStrategy'); +var config = require('./config'); + +class Logger { + + constructor(strategy='toConsole') { + this.logs = []; + this.strategy = LogStrategy[strategy]; + } + + get count() { + return this.logs.length + } + + changeStrategy(newStrategy) { + this.strategy = LogStrategy[newStrategy]; + } + + log(message) { + const timestamp = new Date().toISOString() + this.logs.push({ message, timestamp }) + this.strategy(timestamp, message); + } + +} + +module.exports = new Logger(config.logs.strategy); diff --git a/Node.js-design-patterns/Behaviorals/Strategy/config.json b/Node.js-design-patterns/Behaviorals/Strategy/config.json new file mode 100644 index 0000000..b79d903 --- /dev/null +++ b/Node.js-design-patterns/Behaviorals/Strategy/config.json @@ -0,0 +1,5 @@ +{ + "logs": { + "strategy": "toMorseCode" + } +} diff --git a/Node.js-design-patterns/Behaviorals/Strategy/index.js b/Node.js-design-patterns/Behaviorals/Strategy/index.js new file mode 100644 index 0000000..5449143 --- /dev/null +++ b/Node.js-design-patterns/Behaviorals/Strategy/index.js @@ -0,0 +1,11 @@ +var logger = require('./Logger'); + +logger.log('Hello World'); +logger.log('Hi World'); +logger.log('Yo World'); + +logger.changeStrategy('none'); + +logger.log('Hello World'); +logger.log('Hi World'); +logger.log('Yo World'); diff --git a/Node.js-design-patterns/Behaviorals/Strategy/package-lock.json b/Node.js-design-patterns/Behaviorals/Strategy/package-lock.json new file mode 100644 index 0000000..e9a1854 --- /dev/null +++ b/Node.js-design-patterns/Behaviorals/Strategy/package-lock.json @@ -0,0 +1,33 @@ +{ + "requires": true, + "lockfileVersion": 1, + "dependencies": { + "minimist": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz", + "integrity": "sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8=" + }, + "morse": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/morse/-/morse-0.1.0.tgz", + "integrity": "sha1-HXEWXxWzjbMoWinQNC0w5YLiwUw=", + "requires": { + "optimist": "0.6.1" + } + }, + "optimist": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz", + "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=", + "requires": { + "minimist": "0.0.10", + "wordwrap": "0.0.3" + } + }, + "wordwrap": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz", + "integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc=" + } + } +} diff --git a/Node.js-design-patterns/Behaviorals/readme.md b/Node.js-design-patterns/Behaviorals/readme.md index 45046e5..b57e725 100644 --- a/Node.js-design-patterns/Behaviorals/readme.md +++ b/Node.js-design-patterns/Behaviorals/readme.md @@ -5,4 +5,16 @@ Intent : "Avoid coupling the sender of a request to its receiver by giving than ## Command -Intent : "Encapsulate a request as an object, thereby letting you parameterize with different requests, queue or log requests, and support undoable operations." \ No newline at end of file +Intent : "Encapsulate a request as an object, thereby letting you parameterize with different requests, queue or log requests, and support undoable operations." + +## Iterator + +Intent : "Provide a way to access the elements of an aggregate object sequentially without exposing its underlying representation." + +## Observer + +Intent : "Define a one-to-many dependency between objects so that when object changes state, all its dependents are notified and updated automatically." + +## Strategy + +Intent: "Define a family or algorithms, encapsulate each one, and make then interchangeable. Strategy lets the algorithm vary independently from clients that use it." \ No newline at end of file