├── .gitignore ├── 1 creational ├── 3_prototype.js ├── 4_singleton.js ├── 1_constructor.js └── 2_factory.js ├── 3 behaviour ├── 10_chain_of_responsibility.js ├── 11_comand.js ├── 16_strategy.js ├── 17_template.js ├── 12_iterator.js ├── 13_mediator.js ├── 14_observer.js └── 15_state.js └── 2 structural ├── 9_proxy.js ├── 6_decorator.js ├── 8_flyweight.js ├── 5_adapter.js └── 7_facade.js /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | -------------------------------------------------------------------------------- /1 creational/3_prototype.js: -------------------------------------------------------------------------------- 1 | const car = { 2 | wheels: 4, 3 | 4 | init() { 5 | console.log(`У меня есть ${this.wheels} колеса, мой владелец ${this.owner}`) 6 | } 7 | } 8 | 9 | const carWithOwner = Object.create(car, { 10 | owner: { 11 | value: 'Дмитрий' 12 | } 13 | }) 14 | 15 | console.log(carWithOwner.__proto__ === car) 16 | 17 | carWithOwner.init() 18 | -------------------------------------------------------------------------------- /3 behaviour/10_chain_of_responsibility.js: -------------------------------------------------------------------------------- 1 | class MySum { 2 | constructor(initialValue = 42) { 3 | this.sum = initialValue 4 | } 5 | 6 | add(value) { 7 | this.sum += value 8 | return this 9 | } 10 | } 11 | 12 | const sum1 = new MySum() 13 | console.log(sum1.add(8).add(10).add(1).add(9).sum) 14 | 15 | const sum2 = new MySum(0) 16 | console.log(sum2.add(1).add(2).add(3).sum) 17 | -------------------------------------------------------------------------------- /1 creational/4_singleton.js: -------------------------------------------------------------------------------- 1 | class Database { 2 | constructor(data) { 3 | if (Database.exists) { 4 | return Database.instance 5 | } 6 | Database.instance = this 7 | Database.exists = true 8 | this.data = data 9 | } 10 | 11 | getData() { 12 | return this.data 13 | } 14 | } 15 | 16 | const mongo = new Database('MongoDB') 17 | console.log(mongo.getData()) 18 | 19 | const mysql = new Database('MySQL') 20 | console.log(mysql.getData()) 21 | 22 | 23 | -------------------------------------------------------------------------------- /1 creational/1_constructor.js: -------------------------------------------------------------------------------- 1 | // function Server(name, ip) { 2 | // this.name = name 3 | // this.ip = ip 4 | // } 5 | // 6 | // Server.prototype.getUrl = function() { 7 | // return `https://${this.ip}:80` 8 | // } 9 | 10 | class Server { 11 | constructor(name, ip) { 12 | this.name = name 13 | this.ip = ip 14 | } 15 | 16 | getUrl() { 17 | return `https://${this.ip}:80` 18 | } 19 | } 20 | 21 | const aws = new Server('AWS German', '82.21.21.32') 22 | console.log(aws.getUrl()) 23 | -------------------------------------------------------------------------------- /2 structural/9_proxy.js: -------------------------------------------------------------------------------- 1 | function networkFetch(url) { 2 | return `${url} - Ответ с сервера` 3 | } 4 | 5 | const cache = new Set() 6 | const proxiedFetch = new Proxy(networkFetch, { 7 | apply(target, thisArg, args) { 8 | const url = args[0] 9 | if (cache.has(url)) { 10 | return `${url} - Ответ из кэша` 11 | } else { 12 | cache.add(url) 13 | return Reflect.apply(target, thisArg, args) 14 | } 15 | } 16 | }) 17 | 18 | console.log(proxiedFetch('angular.io')) 19 | console.log(proxiedFetch('react.io')) 20 | console.log(proxiedFetch('angular.io')) 21 | -------------------------------------------------------------------------------- /3 behaviour/11_comand.js: -------------------------------------------------------------------------------- 1 | class MyMath { 2 | constructor(initialValue = 0) { 3 | this.num = initialValue 4 | } 5 | 6 | square() { 7 | return this.num ** 2 8 | } 9 | 10 | cube() { 11 | return this.num ** 3 12 | } 13 | } 14 | 15 | class Command { 16 | constructor(subject) { 17 | this.subject = subject 18 | this.commandsExecuted = [] 19 | } 20 | 21 | execute(command) { 22 | this.commandsExecuted.push(command) 23 | return this.subject[command]() 24 | } 25 | } 26 | 27 | const x = new Command(new MyMath(2)) 28 | 29 | console.log(x.execute('square')) 30 | console.log(x.execute('cube')) 31 | 32 | console.log(x.commandsExecuted) 33 | 34 | -------------------------------------------------------------------------------- /2 structural/6_decorator.js: -------------------------------------------------------------------------------- 1 | class Server { 2 | constructor(ip, port) { 3 | this.ip = ip 4 | this.port = port 5 | } 6 | 7 | get url() { 8 | return `https://${this.ip}:${this.port}` 9 | } 10 | } 11 | 12 | function aws(server) { 13 | server.isAWS = true 14 | server.awsInfo = function() { 15 | return server.url 16 | } 17 | return server 18 | } 19 | 20 | function azure(server) { 21 | server.isAzure = true 22 | server.port += 500 23 | return server 24 | } 25 | 26 | const s1 = aws(new Server('12.34.56.78', 8080)) 27 | console.log(s1.isAWS) 28 | console.log(s1.awsInfo()) 29 | 30 | const s2 = azure(new Server('98.87.76.12', 1000)) 31 | console.log(s2.isAzure) 32 | console.log(s2.url) 33 | -------------------------------------------------------------------------------- /3 behaviour/16_strategy.js: -------------------------------------------------------------------------------- 1 | class Vehicle { 2 | travelTime() { 3 | return this.timeTaken 4 | } 5 | } 6 | 7 | class Bus extends Vehicle { 8 | constructor() { 9 | super() 10 | this.timeTaken = 10 11 | } 12 | } 13 | 14 | class Taxi extends Vehicle { 15 | constructor() { 16 | super() 17 | this.timeTaken = 5 18 | } 19 | } 20 | 21 | class Car extends Vehicle { 22 | constructor() { 23 | super() 24 | this.timeTaken = 3 25 | } 26 | } 27 | 28 | class Commute { 29 | travel(transport) { 30 | return transport.travelTime() 31 | } 32 | } 33 | 34 | const commute = new Commute() 35 | 36 | console.log(commute.travel(new Taxi())) 37 | console.log(commute.travel(new Bus())) 38 | console.log(commute.travel(new Car())) 39 | -------------------------------------------------------------------------------- /2 structural/8_flyweight.js: -------------------------------------------------------------------------------- 1 | class Car { 2 | constructor(model, price) { 3 | this.model = model 4 | this.price = price 5 | } 6 | } 7 | 8 | class CarFactory { 9 | constructor() { 10 | this.cars = [] 11 | } 12 | 13 | create(model, price) { 14 | const candidate = this.getCar(model) 15 | if (candidate) { 16 | return candidate 17 | } 18 | 19 | const newCar = new Car(model, price) 20 | this.cars.push(newCar) 21 | return newCar 22 | } 23 | 24 | getCar(model) { 25 | return this.cars.find(car => car.model === model) 26 | } 27 | } 28 | 29 | const factory = new CarFactory() 30 | 31 | const bmwX6 = factory.create('bmw', 10000) 32 | const audi = factory.create('audi', 12000) 33 | const bmwX3 = factory.create('bmw', 8000) 34 | 35 | console.log(bmwX3 === bmwX6) 36 | 37 | -------------------------------------------------------------------------------- /2 structural/5_adapter.js: -------------------------------------------------------------------------------- 1 | class OldCalc { 2 | operations(t1, t2, operation) { 3 | switch (operation) { 4 | case 'add': return t1 + t2 5 | case 'sub': return t1 - t2 6 | default: return NaN 7 | } 8 | } 9 | } 10 | 11 | class NewCalc { 12 | add(t1, t2) { 13 | return t1 + t2 14 | } 15 | 16 | sub(t1, t2) { 17 | return t1 - t2 18 | } 19 | } 20 | 21 | class CalcAdapter { 22 | constructor() { 23 | this.calc = new NewCalc() 24 | } 25 | 26 | operations(t1, t2, operation) { 27 | switch (operation) { 28 | case 'add': return this.calc.add(t1, t2) 29 | case 'sub': return this.calc.sub(t1, t2) 30 | default: return NaN 31 | } 32 | } 33 | } 34 | 35 | const oldCalc = new OldCalc() 36 | console.log(oldCalc.operations(10, 5, 'add')) 37 | 38 | const newCalc = new NewCalc() 39 | console.log(newCalc.add(10, 5)) 40 | 41 | const adapter = new CalcAdapter() 42 | console.log(adapter.operations(25, 10, 'sub')) 43 | 44 | 45 | -------------------------------------------------------------------------------- /3 behaviour/17_template.js: -------------------------------------------------------------------------------- 1 | class Employee { 2 | constructor(name, salary) { 3 | this.name = name 4 | this.salary = salary 5 | } 6 | 7 | responsibilities() {} 8 | 9 | work() { 10 | return `${this.name} выполняет ${this.responsibilities()}` 11 | } 12 | 13 | getPaid() { 14 | return `${this.name} имеет ЗП ${this.salary}` 15 | } 16 | } 17 | 18 | class Developer extends Employee { 19 | constructor(name, salary) { 20 | super(name, salary) 21 | } 22 | 23 | responsibilities() { 24 | return 'процесс создания программ' 25 | } 26 | } 27 | 28 | class Tester extends Employee { 29 | constructor(name, salary) { 30 | super(name, salary) 31 | } 32 | 33 | responsibilities() { 34 | return 'процесс тестирования' 35 | } 36 | } 37 | 38 | const dev = new Developer('Владилен', 100000) 39 | console.log(dev.getPaid()) 40 | console.log(dev.work()) 41 | 42 | const tester = new Tester('Виктория', 90000) 43 | console.log(tester.getPaid()) 44 | console.log(tester.work()) 45 | -------------------------------------------------------------------------------- /3 behaviour/12_iterator.js: -------------------------------------------------------------------------------- 1 | class MyIterator { 2 | constructor(data) { 3 | this.index = 0 4 | this.data = data 5 | } 6 | 7 | [Symbol.iterator]() { 8 | return { 9 | next: () => { 10 | if (this.index < this.data.length) { 11 | return { 12 | value: this.data[this.index++], 13 | done: false 14 | } 15 | } else { 16 | this.index = 0 17 | return { 18 | done: true, 19 | value: void 0 20 | } 21 | } 22 | } 23 | } 24 | } 25 | } 26 | 27 | function* generator(collection) { 28 | let index = 0 29 | 30 | while (index < collection.length) { 31 | yield collection[index++] 32 | } 33 | } 34 | 35 | 36 | const iterator = new MyIterator(['This', 'is', 'iterator']) 37 | const gen = generator(['This', 'is', 'iterator']) 38 | 39 | // for (const val of gen) { 40 | // console.log('Value: ', val) 41 | // } 42 | 43 | console.log(gen.next().value) 44 | console.log(gen.next().value) 45 | console.log(gen.next().value) 46 | 47 | 48 | -------------------------------------------------------------------------------- /3 behaviour/13_mediator.js: -------------------------------------------------------------------------------- 1 | class User { 2 | constructor(name) { 3 | this.name = name 4 | this.room = null 5 | } 6 | 7 | send(message, to) { 8 | this.room.send(message, this, to) 9 | } 10 | 11 | receive(message, from) { 12 | console.log(`${from.name} => ${this.name}: ${message}`) 13 | } 14 | } 15 | 16 | class ChatRoom { 17 | constructor() { 18 | this.users = {} 19 | } 20 | 21 | register(user) { 22 | this.users[user.name] = user 23 | user.room = this 24 | } 25 | 26 | send(message, from, to) { 27 | if (to) { 28 | to.receive(message, from) 29 | } else { 30 | Object.keys(this.users).forEach(key => { 31 | if (this.users[key] !== from) { 32 | this.users[key].receive(message, from) 33 | } 34 | }) 35 | } 36 | } 37 | } 38 | 39 | const vlad = new User('Vladilen') 40 | const lena = new User('Elena') 41 | const igor = new User('Igor') 42 | 43 | const room = new ChatRoom() 44 | 45 | room.register(vlad) 46 | room.register(lena) 47 | room.register(igor) 48 | 49 | vlad.send('Hello!', lena) 50 | lena.send('Hello hello!', vlad) 51 | igor.send('Vsem privet') 52 | -------------------------------------------------------------------------------- /2 structural/7_facade.js: -------------------------------------------------------------------------------- 1 | class Complaints { 2 | constructor() { 3 | this.complaints = [] 4 | } 5 | 6 | reply(complaint) {} 7 | 8 | add(complaint) { 9 | this.complaints.push(complaint) 10 | return this.reply(complaint) 11 | } 12 | } 13 | 14 | class ProductComplaints extends Complaints { 15 | reply({id, customer, details}) { 16 | return `Product: ${id}: ${customer} (${details})` 17 | } 18 | } 19 | 20 | class ServiceComplaints extends Complaints { 21 | reply({id, customer, details}) { 22 | return `Service: ${id}: ${customer} (${details})` 23 | } 24 | } 25 | 26 | class ComplaintRegistry { 27 | register(customer, type, details) { 28 | const id = Date.now() 29 | let complaint 30 | 31 | if (type === 'service') { 32 | complaint = new ServiceComplaints() 33 | } else { 34 | complaint = new ProductComplaints() 35 | } 36 | 37 | return complaint.add({id, customer, details}) 38 | } 39 | } 40 | 41 | const registry = new ComplaintRegistry() 42 | 43 | console.log(registry.register('Vladilen', 'service', 'недоступен')) 44 | console.log(registry.register('Elena', 'product', 'вылазит ошибка')) 45 | 46 | -------------------------------------------------------------------------------- /1 creational/2_factory.js: -------------------------------------------------------------------------------- 1 | class SimpleMembership { 2 | constructor(name) { 3 | this.name = name 4 | this.cost = 50 5 | } 6 | } 7 | 8 | class StandardMembership { 9 | constructor(name) { 10 | this.name = name 11 | this.cost = 150 12 | } 13 | } 14 | 15 | class PremiumMembership { 16 | constructor(name) { 17 | this.name = name 18 | this.cost = 500 19 | } 20 | } 21 | 22 | class MemberFactory { 23 | static list = { 24 | simple: SimpleMembership, 25 | standard: StandardMembership, 26 | premium: PremiumMembership 27 | } 28 | 29 | create(name, type = 'simple') { 30 | const Membership = MemberFactory.list[type] || MemberFactory.list.simple 31 | const member = new Membership(name) 32 | member.type = type 33 | member.define = function() { 34 | console.log(`${this.name} (${this.type}): ${this.cost}`) 35 | } 36 | return member 37 | } 38 | } 39 | 40 | const factory = new MemberFactory() 41 | 42 | const members = [ 43 | factory.create('Vladilen', 'simple'), 44 | factory.create('Elena', 'premium'), 45 | factory.create('Vasilisa', 'standard'), 46 | factory.create('Ivan', 'premium'), 47 | factory.create('Petr') 48 | ] 49 | 50 | members.forEach(m => { 51 | m.define() 52 | }) 53 | -------------------------------------------------------------------------------- /3 behaviour/14_observer.js: -------------------------------------------------------------------------------- 1 | class Subject { 2 | constructor() { 3 | this.observers = [] 4 | } 5 | 6 | subscribe(observer) { 7 | this.observers.push(observer) 8 | } 9 | 10 | unsubscribe(observer) { 11 | this.observers = this.observers.filter(obs => obs !== observer) 12 | } 13 | 14 | fire(action) { 15 | this.observers.forEach(observer => { 16 | observer.update(action) 17 | }) 18 | } 19 | } 20 | 21 | class Observer { 22 | constructor(state = 1) { 23 | this.state = state 24 | this.initialState = state 25 | } 26 | 27 | update(action) { 28 | switch (action.type) { 29 | case 'INCREMENT': 30 | this.state = ++this.state 31 | break 32 | case 'DECREMENT': 33 | this.state = --this.state 34 | break 35 | case 'ADD': 36 | this.state += action.payload 37 | break 38 | default: 39 | this.state = this.initialState 40 | } 41 | } 42 | } 43 | 44 | const stream$ = new Subject() 45 | 46 | const obs1 = new Observer() 47 | const obs2 = new Observer(42) 48 | 49 | stream$.subscribe(obs1) 50 | stream$.subscribe(obs2) 51 | 52 | stream$.fire({type: 'INCREMENT'}) 53 | stream$.fire({type: 'INCREMENT'}) 54 | stream$.fire({type: 'DECREMENT'}) 55 | stream$.fire({type: 'ADD', payload: 10}) 56 | 57 | console.log(obs1.state) 58 | console.log(obs2.state) 59 | -------------------------------------------------------------------------------- /3 behaviour/15_state.js: -------------------------------------------------------------------------------- 1 | class Light { 2 | constructor(light) { 3 | this.light = light 4 | } 5 | } 6 | 7 | class RedLight extends Light { 8 | constructor() { 9 | super('red') 10 | } 11 | 12 | sign() { 13 | return 'СТОП' 14 | } 15 | } 16 | 17 | class YellowLight extends Light { 18 | constructor() { 19 | super('yellow') 20 | } 21 | 22 | sign() { 23 | return 'ГОТОВЬСЯ' 24 | } 25 | } 26 | 27 | class GreenLight extends Light { 28 | constructor() { 29 | super('green') 30 | } 31 | 32 | sign() { 33 | return 'ЕДЬ!' 34 | } 35 | } 36 | 37 | class TrafficLight { 38 | constructor() { 39 | this.states = [ 40 | new RedLight(), 41 | new YellowLight(), 42 | new GreenLight() 43 | ] 44 | this.current = this.states[0] 45 | } 46 | 47 | change() { 48 | const total = this.states.length 49 | let index = this.states.findIndex(light => light === this.current) 50 | 51 | if (index + 1 < total) { 52 | this.current = this.states[index + 1] 53 | } else { 54 | this.current = this.states[0] 55 | } 56 | } 57 | 58 | sign() { 59 | return this.current.sign() 60 | } 61 | } 62 | 63 | const traffic = new TrafficLight() 64 | console.log(traffic.sign()) 65 | traffic.change() 66 | 67 | console.log(traffic.sign()) 68 | traffic.change() 69 | 70 | console.log(traffic.sign()) 71 | traffic.change() 72 | 73 | console.log(traffic.sign()) 74 | traffic.change() 75 | 76 | console.log(traffic.sign()) 77 | traffic.change() 78 | 79 | console.log(traffic.sign()) 80 | traffic.change() 81 | --------------------------------------------------------------------------------