├── iterator_pattern ├── iterator_pattern.js └── iterrator_pattern_v2.js ├── .github └── FUNDING.yml ├── prototype_pattern └── prototype_pattern.js ├── singleton_pattern └── index.js ├── facade_pattern └── facade_pattern.js ├── factory_pattern ├── factory_pattern.js └── factory_pattern_2.js ├── LICENSE ├── README.md ├── .vscode └── settings.json ├── repository_pattern └── index.js ├── command_pattern ├── command_pattern.js └── command_pattern_v2.js ├── observer_pattern └── observer_pattern.js ├── strategy_pattern └── index.js ├── state_pattern └── index.js ├── proxy_pattern └── index.js └── saga_pattern └── index.js /iterator_pattern/iterator_pattern.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | class Iterator { 4 | constructor(collection) { 5 | this.collection = collection; 6 | this.index = 0; 7 | } 8 | 9 | hasNext() { 10 | return this.index < this.collection.length; 11 | } 12 | 13 | next() { 14 | return this.hasNext() ? this.collection[this.index++] : null; 15 | } 16 | } 17 | 18 | // Sử dụng Iterator để lặp qua một mảng 19 | const names = ["Nguyễn Tiến Tài", "Nguyễn Duy Thịnh", "Thái Văn Nam"]; 20 | const iterator = new Iterator(names); 21 | 22 | while (iterator.hasNext()) { 23 | const name = iterator.next(); 24 | console.log(name); 25 | } 26 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [fdhhhdjd]# Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 4 | patreon: user?u=65668237 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: tientainguyen 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | custom: ["https://profile-forme.cf"] -------------------------------------------------------------------------------- /prototype_pattern/prototype_pattern.js: -------------------------------------------------------------------------------- 1 | class Person { 2 | constructor(name, age) { 3 | this.name = name; 4 | this.age = age; 5 | } 6 | introduce() { 7 | return console.log( 8 | `Hi, my name is ${this.name} and I am ${this.age} years old.` 9 | ); 10 | } 11 | clone() { 12 | return new Person(this.name, this.age); 13 | } 14 | } 15 | 16 | const pershatonOne = new Person("Tiến Tài", 23); 17 | const personTwo = personOne.clone(); 18 | personTwo.name = "Nguyen Tien Tai"; 19 | personTwo.age = 22; 20 | 21 | personOne.introduce(); // Hi, my name is Tiến Tài and I am 23 years old. 22 | personTwo.introduce(); // Hi, my name is Tài Dev and I am 25 years old. 23 | -------------------------------------------------------------------------------- /singleton_pattern/index.js: -------------------------------------------------------------------------------- 1 | class Database { 2 | static connections = []; 3 | 4 | constructor(data) { 5 | const existingConnection = Database.connections.find( 6 | (conn) => conn.data === data 7 | ); 8 | if (existingConnection) { 9 | return existingConnection; 10 | } 11 | this.data = data; 12 | Database.connections.push(this); 13 | return this; 14 | } 15 | 16 | static getDataByIndex(index) { 17 | return Database.connections[index].data; 18 | } 19 | } 20 | 21 | const mongo = new Database("mongo"); 22 | const mysql = new Database("mysql"); 23 | const mysql2 = new Database("mysql2"); 24 | 25 | console.log(Database.getDataByIndex(0)); // "mongo" 26 | console.log(Database.getDataByIndex(1)); // "mysql" 27 | console.log(Database.getDataByIndex(2)); // "mysql2" 28 | -------------------------------------------------------------------------------- /facade_pattern/facade_pattern.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | // Define complex subsystems 4 | class HotelBooking { 5 | bookHotel() { 6 | console.log("Hotel booked."); 7 | } 8 | } 9 | 10 | class FlightBooking { 11 | bookFlight() { 12 | console.log("Flight booked."); 13 | } 14 | } 15 | 16 | class CarRental { 17 | rentCar() { 18 | console.log("Car rented."); 19 | } 20 | } 21 | 22 | // Define complex subsystems 23 | class TravelFacade { 24 | constructor() { 25 | this.hotelBooking = new HotelBooking(); 26 | this.flightBooking = new FlightBooking(); 27 | this.carRental = new CarRental(); 28 | } 29 | bookTravel() { 30 | this.hotelBooking.bookHotel(); 31 | this.flightBooking.bookFlight(); 32 | this.carRental.rentCar(); 33 | 34 | console.log("Travel booked successfully!"); 35 | } 36 | } 37 | 38 | // Use facade class to book travel 39 | const travelFacade = new TravelFacade(); 40 | travelFacade.bookTravel(); 41 | -------------------------------------------------------------------------------- /factory_pattern/factory_pattern.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | class Student { 4 | constructor(name, age) { 5 | this.name = name; 6 | this.age = age; 7 | } 8 | } 9 | 10 | class MathClass { 11 | constructor(students) { 12 | this.students = students; 13 | } 14 | } 15 | 16 | class EnglishClass { 17 | constructor(students) { 18 | this.students = students; 19 | } 20 | } 21 | 22 | class ClassFactory { 23 | static createMathClass(students) { 24 | return new MathClass(students); 25 | } 26 | 27 | static createEnglishClass(students) { 28 | return new EnglishClass(students); 29 | } 30 | } 31 | // Sử dụng ClassFactory để tạo đối tượng lớp học và học sinh 32 | const studentOne = new Student("Nguyễn Tiến Tài", 23); 33 | const studentTwo = new Student("Nguyễn Thị Thu Hiền", 15); 34 | 35 | const mathClass = ClassFactory.createMathClass([studentOne, studentTwo]); 36 | const englishClass = ClassFactory.createEnglishClass([studentOne]); 37 | console.log(mathClass); 38 | console.log(englishClass); 39 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Nguyễn Tiến Tài 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 |
2 | 3 | 9 | 10 | ## Study With Me: Study Desgin Pattern 11 | 12 | ## Team Word: Liên hệ công việc https://profile-forme.cf 13 | 14 | ## 1. Nguyễn Tiến Tài ( Maintanin 🚩). 15 | 16 | ## Tài Khoản Donate li Cf 🥛,để có động lực code cho anh em tham khảo 😄😄. 17 | 18 |  19 | 20 | ## Mk: NGUYEN TIEN TAI 21 | 22 | ## STK: 1651002972052 23 | 24 | ## Chi Nhánh: NGAN HANG TMCP AN BINH (ABBANK). 25 | 26 | ## SUPORT CONTACT: [https://profile-forme.cf](https://profile-forme.cf) 27 | 28 | ## Thank You <3. 29 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | // Auto Format code 3 | "editor.formatOnSave": true, 4 | 5 | // Used Prettier format code 6 | "editor.defaultFormatter": "esbenp.prettier-vscode", 7 | 8 | // Update module import path when moving files. 9 | "javascript.updateImportsOnFileMove.enabled": "always", 10 | 11 | // Highlight brackets when the cursor is inside them 12 | "editor.guides.bracketPairs": "active", 13 | 14 | // Check syntax errors and errors in code with ESLint for JS and TS files. 15 | "eslint.validate": [ 16 | "javascript", 17 | "javascriptreact", 18 | "typescript", 19 | "typescriptreact" 20 | ], 21 | 22 | // Automatically correct errors reported in the code when saving the file. 23 | "editor.codeActionsOnSave": { 24 | "source.fixAll": "explicit" 25 | }, 26 | 27 | // Set the newline character for files to be a newline (LF). 28 | "files.eol": "\n", 29 | 30 | // Use Extendtion SQL as default code format for SQL files. 31 | "[sql]": { 32 | "editor.defaultFormatter": "adpyke.vscode-sql-formatter" 33 | }, 34 | 35 | // cSpell spell check list of words to skip. 36 | "cSpell.words": [ 37 | "argothim", 38 | "Localstorage", 39 | "MIDDLAWARE", 40 | "Middlawre", 41 | "mutilp" 42 | ], 43 | "[dockerfile]": { 44 | "editor.defaultFormatter": "ms-azuretools.vscode-docker" 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /repository_pattern/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | class UserRepository { 4 | constructor(database) { 5 | this.database = database; 6 | } 7 | 8 | async getUserById(id) { 9 | const user = await this.database.query( 10 | `SELECT * FROM users WHERE id = ${id}` 11 | ); 12 | return user; 13 | } 14 | 15 | async createUser(user) { 16 | const result = await this.database.query( 17 | `INSERT INTO users (name, email) VALUES ('${user.name}', '${user.email}')` 18 | ); 19 | return result; 20 | } 21 | 22 | async updateUser(id, user) { 23 | const result = await this.database.query( 24 | `UPDATE users SET name = '${user.name}', email = '${user.email}' WHERE id = ${id}` 25 | ); 26 | return result; 27 | } 28 | 29 | async deleteUser(id) { 30 | const result = await this.database.query( 31 | `DELETE FROM users WHERE id = ${id}` 32 | ); 33 | return result; 34 | } 35 | } 36 | 37 | // Sử dụng UserRepository 38 | const userRepository = new UserRepository(database); 39 | 40 | const user = await userRepository.getUserById(1); 41 | console.log(user); 42 | 43 | const newUser = { name: "Nguyễn Tiến Tài", email: "nguyentientai@gmail.com" }; 44 | await userRepository.createUser(newUser); 45 | 46 | const updatedUser = { name: "Tài Heo", email: "nguyentientai@gmail.com" }; 47 | await userRepository.updateUser(1, updatedUser); 48 | 49 | await userRepository.deleteUser(1); 50 | -------------------------------------------------------------------------------- /command_pattern/command_pattern.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | class RemoteControl { 3 | constructor() { 4 | this.turnOnCommand = null; 5 | this.turnOffCommand = null; 6 | } 7 | 8 | setTurnOnCommand(command) { 9 | this.turnOnCommand = command; 10 | } 11 | 12 | setTurnOffCommand(command) { 13 | this.turnOffCommand = command; 14 | } 15 | 16 | turnOn() { 17 | this.turnOnCommand.execute(); 18 | } 19 | 20 | turnOff() { 21 | this.turnOffCommand.execute(); 22 | } 23 | } 24 | 25 | class Light { 26 | turnOn() { 27 | console.log("Light is on"); 28 | } 29 | 30 | turnOff() { 31 | console.log("Light is off"); 32 | } 33 | } 34 | 35 | class TurnOnCommand { 36 | constructor(light) { 37 | this.light = light; 38 | } 39 | 40 | execute() { 41 | this.light.turnOn(); 42 | } 43 | } 44 | 45 | class TurnOffCommand { 46 | constructor(light) { 47 | this.light = light; 48 | } 49 | 50 | execute() { 51 | this.light.turnOff(); 52 | } 53 | } 54 | 55 | // Sử dụng 56 | const light = new Light(); 57 | const turnOnCommand = new TurnOnCommand(light); 58 | const turnOffCommand = new TurnOffCommand(light); 59 | 60 | console.log(turnOnCommand); 61 | const remoteControl = new RemoteControl(); 62 | remoteControl.setTurnOnCommand(turnOnCommand); 63 | remoteControl.setTurnOffCommand(turnOffCommand); 64 | 65 | remoteControl.turnOn(); // In ra "Light is on" 66 | remoteControl.turnOff(); // In ra "Light is off" 67 | -------------------------------------------------------------------------------- /factory_pattern/factory_pattern_2.js: -------------------------------------------------------------------------------- 1 | class Product { 2 | constructor(name, price) { 3 | this.name = name; 4 | this.price = price; 5 | } 6 | getInfo() { 7 | return `Product: ${this.name}, Price: ${this.price}`; 8 | } 9 | } 10 | 11 | class Phone extends Product { 12 | constructor(name, price, brand, model) { 13 | super(name, price); 14 | this.brand = brand; 15 | this.model = model; 16 | } 17 | getInfo() { 18 | return `${super.getInfo()},Brand: ${this.brand}, Model: ${this.model}`; 19 | } 20 | } 21 | 22 | class Shirt extends Product { 23 | constructor(name, price, size, color) { 24 | super(name, price); 25 | this.size = size; 26 | this.color = color; 27 | } 28 | 29 | getInfo() { 30 | return `${super.getInfo()}, Size: ${this.size}, Color: ${this.color}`; 31 | } 32 | } 33 | 34 | class ProductFactory { 35 | createProduct(type, ...arg) { 36 | switch (type) { 37 | case "phone": 38 | return new Phone(...arg); 39 | case "shirt": 40 | return new Shirt(...arg); 41 | default: 42 | throw new Error("Invalid product type."); 43 | } 44 | } 45 | } 46 | 47 | const factory = new ProductFactory(); 48 | 49 | const newPhone = factory.createProduct( 50 | "phone", 51 | "I Phone 10", 52 | 1000, 53 | "apple", 54 | "iphone" 55 | ); 56 | console.log(newPhone.getInfo()); 57 | 58 | const newShirt = factory.createProduct("shirt", "T-shirt", 20, "M", "Blue"); 59 | console.log(newShirt.getInfo()); 60 | -------------------------------------------------------------------------------- /observer_pattern/observer_pattern.js: -------------------------------------------------------------------------------- 1 | class Subject { 2 | constructor() { 3 | this.observers = []; 4 | } 5 | subscribe(observer) { 6 | this.observers.push(observer); 7 | } 8 | 9 | unsubscribe(observer) { 10 | const index = this.observers.indexOf(observer); 11 | let removedObserver; 12 | if (index > -1) { 13 | removedObserver = this.observers.splice(index, 1)[0]; 14 | } 15 | return console.log( 16 | `${removedObserver.name} received message: Unsubscribed` 17 | ); 18 | } 19 | 20 | notify(message) { 21 | this.observers.forEach((observer) => observer.update(message)); 22 | } 23 | } 24 | 25 | class Observer { 26 | constructor(name) { 27 | this.name = name; 28 | } 29 | 30 | update(message) { 31 | console.log(`${this.name} received message: ${message}`); 32 | } 33 | } 34 | 35 | // Tạo một Subject mới 36 | const subject = new Subject(); 37 | 38 | // Tạo các Observer mới 39 | const observer1 = new Observer("Nguyễn Tiến Tài"); 40 | const observer2 = new Observer("Lê Bảo"); 41 | const observer3 = new Observer("Sơn Tùng"); 42 | 43 | // Đăng ký các Observer cho Subject 44 | subject.subscribe(observer1); 45 | subject.subscribe(observer2); 46 | subject.subscribe(observer3); 47 | 48 | // Thông báo cho các Observer về sự kiện mới 49 | subject.notify("subscribed"); 50 | 51 | // Hủy đăng ký Observer2 52 | subject.unsubscribe(observer2); 53 | 54 | // Thông báo cho các Observer về sự kiện mới 55 | subject.notify("Only subscribed"); 56 | -------------------------------------------------------------------------------- /strategy_pattern/index.js: -------------------------------------------------------------------------------- 1 | // Các chiến lược tính giá cước giao hàng 2 | class StandardShipping { 3 | calculateCost(order) { 4 | return order.weight * 10; 5 | } 6 | } 7 | 8 | class ExpressShipping { 9 | calculateCost(order) { 10 | return order.weight * 20 + 10; // Giao nhanh với chi phí thêm 11 | } 12 | } 13 | 14 | class PostalShipping { 15 | calculateCost(order) { 16 | return order.weight * 5; // Giao qua bưu điện rẻ hơn 17 | } 18 | } 19 | 20 | // Context: Lớp này sử dụng các chiến lược tính phí vận chuyển 21 | class ShippingCost { 22 | constructor(strategy) { 23 | this.strategy = strategy; 24 | } 25 | 26 | setStrategy(strategy) { 27 | this.strategy = strategy; 28 | } 29 | 30 | calculate(order) { 31 | return this.strategy.calculateCost(order); 32 | } 33 | } 34 | 35 | // Ví dụ sử dụng 36 | const order = { weight: 5 }; // Đơn hàng có trọng lượng 5kg 37 | 38 | const standardShipping = new StandardShipping(); 39 | const expressShipping = new ExpressShipping(); 40 | const postalShipping = new PostalShipping(); 41 | 42 | const shippingCost = new ShippingCost(standardShipping); 43 | 44 | console.log("Standard Shipping: ", shippingCost.calculate(order)); // Kết quả dựa trên StandardShipping 45 | 46 | // Đổi sang Express Shipping 47 | shippingCost.setStrategy(expressShipping); 48 | console.log("Express Shipping: ", shippingCost.calculate(order)); // Kết quả dựa trên ExpressShipping 49 | 50 | // Đổi sang Postal Shipping 51 | shippingCost.setStrategy(postalShipping); 52 | console.log("Postal Shipping: ", shippingCost.calculate(order)); // Kết quả dựa trên PostalShipping 53 | -------------------------------------------------------------------------------- /command_pattern/command_pattern_v2.js: -------------------------------------------------------------------------------- 1 | // Lớp đại diện cho yêu cầu (command) 2 | class OrderCommand { 3 | constructor(order) { 4 | this.order = order; 5 | } 6 | 7 | execute() { 8 | // Mặc định, không làm gì cả 9 | } 10 | } 11 | // Lớp đại diện cho yêu cầu gửi đơn hàng 12 | class ShipOrderCommand extends OrderCommand { 13 | execute() { 14 | console.log(`Shipping order: ${this.order}`); 15 | // Code để thực hiện việc gửi đơn hàng 16 | } 17 | } 18 | 19 | // Lớp đại diện cho yêu cầu hủy đơn hàng 20 | class CancelOrderCommand extends OrderCommand { 21 | execute() { 22 | console.log(`Canceling order: ${this.order}`); 23 | // Code để thực hiện việc hủy đơn hàng 24 | } 25 | } 26 | 27 | // Lớp quản lý yêu cầu (command) 28 | class CommandManager { 29 | constructor() { 30 | this.commands = []; 31 | } 32 | 33 | addCommand(command) { 34 | this.commands.push(command); 35 | } 36 | 37 | executeCommands() { 38 | for (const command of this.commands) { 39 | command.execute(); 40 | } 41 | this.commands = []; 42 | } 43 | } 44 | 45 | // Sử dụng Command Pattern 46 | const commandManager = new CommandManager(); 47 | 48 | const order1 = "ORDER123"; 49 | const order2 = "ORDER456"; 50 | 51 | const shipOrderCommand1 = new ShipOrderCommand(order1); 52 | const shipOrderCommand2 = new ShipOrderCommand(order2); 53 | const cancelOrderCommand = new CancelOrderCommand(order2); 54 | 55 | commandManager.addCommand(shipOrderCommand1); 56 | commandManager.addCommand(shipOrderCommand2); 57 | commandManager.addCommand(cancelOrderCommand); 58 | 59 | commandManager.executeCommands(); 60 | -------------------------------------------------------------------------------- /iterator_pattern/iterrator_pattern_v2.js: -------------------------------------------------------------------------------- 1 | class Product { 2 | constructor(name, price) { 3 | this.name = name; 4 | this.price = price; 5 | } 6 | } 7 | 8 | // Lớp danh sách sản phẩm trong E-commerce 9 | class ProductList { 10 | constructor() { 11 | this.products = []; 12 | } 13 | 14 | addProduct(product) { 15 | this.products.push(product); 16 | } 17 | 18 | // Phương thức tạo Iterator cho danh sách sản phẩm 19 | createIterator() { 20 | return new ProductIterator(this.products); 21 | } 22 | } 23 | 24 | // Lớp Iterator để duyệt qua danh sách sản phẩm 25 | class ProductIterator { 26 | constructor(products) { 27 | this.products = products; 28 | this.index = 0; 29 | } 30 | 31 | // Phương thức kiểm tra xem còn phần tử tiếp theo hay không 32 | hasNext() { 33 | return this.index < this.products.length; 34 | } 35 | 36 | // Phương thức lấy phần tử tiếp theo trong danh sách 37 | next() { 38 | if (this.hasNext()) { 39 | const product = this.products[this.index]; 40 | this.index++; 41 | return product; 42 | } 43 | return null; 44 | } 45 | } 46 | 47 | const product1 = new Product("Phone", 1000); 48 | const product2 = new Product("Laptop", 1500); 49 | const product3 = new Product("Shirt", 50); 50 | 51 | // Sử dụng Iterator Pattern 52 | const productList = new ProductList(); 53 | 54 | productList.addProduct(product1); 55 | productList.addProduct(product2); 56 | productList.addProduct(product3); 57 | 58 | const iterator = productList.createIterator(); 59 | while (iterator.hasNext()) { 60 | const product = iterator.next(); 61 | console.log(`Product: ${product.name}, Price: ${product.price}`); 62 | } 63 | -------------------------------------------------------------------------------- /state_pattern/index.js: -------------------------------------------------------------------------------- 1 | // Lớp trạng thái của đơn hàng 2 | class PendingState { 3 | handle(order) { 4 | console.log("Đơn hàng đang chờ xử lý."); 5 | order.setState(new ShippingState()); // Chuyển sang trạng thái đang vận chuyển 6 | } 7 | 8 | cancel(order) { 9 | console.log("Đơn hàng đã bị hủy khi đang chờ xử lý."); 10 | order.setState(new CancelledState()); 11 | } 12 | } 13 | 14 | class ShippingState { 15 | handle(order) { 16 | console.log("Đơn hàng đang được vận chuyển."); 17 | order.setState(new DeliveredState()); // Chuyển sang trạng thái đã giao hàng 18 | } 19 | 20 | cancel(order) { 21 | console.log("Không thể hủy đơn hàng khi đang vận chuyển."); 22 | } 23 | } 24 | 25 | class DeliveredState { 26 | handle(order) { 27 | console.log("Đơn hàng đã được giao."); 28 | } 29 | 30 | cancel(order) { 31 | console.log("Không thể hủy đơn hàng đã được giao."); 32 | } 33 | } 34 | 35 | class CancelledState { 36 | handle(order) { 37 | console.log("Đơn hàng đã bị hủy."); 38 | } 39 | 40 | cancel(order) { 41 | console.log("Đơn hàng đã bị hủy trước đó."); 42 | } 43 | } 44 | 45 | class Order { 46 | constructor() { 47 | this.state = new PendingState(); // Trạng thái ban đầu là đang chờ xử lý 48 | } 49 | 50 | setState(state) { 51 | this.state = state; 52 | } 53 | 54 | process() { 55 | this.state.handle(this); // Xử lý đơn hàng dựa trên trạng thái hiện tại 56 | } 57 | 58 | cancel() { 59 | this.state.cancel(this); // Hủy đơn hàng dựa trên trạng thái hiện tại 60 | } 61 | } 62 | 63 | const order = new Order(); 64 | 65 | order.process(); // Đơn hàng đang chờ xử lý -> Chuyển sang Đang vận chuyển 66 | order.cancel(); // Không thể hủy đơn hàng khi đang vận chuyển 67 | order.process(); // Đơn hàng đang được vận chuyển -> Chuyển sang Đã giao hàng 68 | order.cancel(); // Không thể hủy đơn hàng đã được giao 69 | -------------------------------------------------------------------------------- /proxy_pattern/index.js: -------------------------------------------------------------------------------- 1 | // * v1 2 | class Person { 3 | constructor({ name, age }) { 4 | this.name = name; 5 | this.age = age; 6 | } 7 | } 8 | 9 | class PersonProxy { 10 | constructor(person) { 11 | this.person = person; 12 | this.checkProperties(); 13 | } 14 | checkProperties() { 15 | for (const [prop, value] of Object.entries(this.person)) { 16 | if (value == null) console.log(`${prop} is null`); 17 | if (value === undefined) console.log(`${prop} is undefined`); 18 | } 19 | } 20 | 21 | isPropertyValid(prop) { 22 | if (!this.person || !(prop in this.person)) { 23 | console.log( 24 | `Hmm.. this property doesn't seem to exist on the target object` 25 | ); 26 | return false; 27 | } 28 | const value = this.person[prop]; 29 | if (value === undefined || value === null) { 30 | console.log(`Hmm.. this property doesn't seem to have a valid value`); 31 | return false; 32 | } 33 | return true; 34 | } 35 | 36 | get(prop) { 37 | if (!this.isPropertyValid(prop)) return; 38 | return console.log(`The value of ${prop} is ${this.person[prop]}`); 39 | } 40 | 41 | set(prop, value) { 42 | if (!this.isPropertyValid(prop)) return; 43 | 44 | if (prop === "age" && typeof value !== "number") { 45 | console.log(`Sorry, you can only pass numeric values for age.`); 46 | } else if (prop === "name" && value.length < 2) { 47 | console.log(`You need to provide a valid name.`); 48 | } else { 49 | console.log(`Changed ${prop} from ${this.person[prop]} to ${value}.`); 50 | this.person[prop] = value; 51 | } 52 | } 53 | } 54 | 55 | const person = new Person({ name: "Tai dev", age: 23 }); 56 | const personProxy = new PersonProxy(person); 57 | 58 | personProxy.get("name"); 59 | personProxy.set("name", "Nguyen Tien Tai"); 60 | -------------------------------------------------------------------------------- /saga_pattern/index.js: -------------------------------------------------------------------------------- 1 | class SagaCoordinator { 2 | constructor() { 3 | this.state = new BasketState(this); 4 | this.orderData = {}; 5 | } 6 | 7 | changeState(state) { 8 | this.state = state; 9 | } 10 | 11 | async start(data) { 12 | this.orderData = data; 13 | try { 14 | await this.state.execute(); 15 | } catch (error) { 16 | console.error("Saga failed, rolling back:", error.message); 17 | await this.rollback(); 18 | throw new Error("Saga failed and rollback executed"); 19 | } 20 | } 21 | 22 | async rollback() { 23 | await this.state.rollback(); 24 | } 25 | } 26 | 27 | class BasketState { 28 | constructor(coordinator) { 29 | this.coordinator = coordinator; 30 | } 31 | 32 | async execute() { 33 | console.log("Adding items to basket"); 34 | // Simulate async operation and potential failure 35 | setTimeout(async () => { 36 | try { 37 | this.coordinator.changeState(new OrderState(this.coordinator)); 38 | await this.coordinator.start(this.coordinator.orderData); 39 | } catch (error) { 40 | throw new Error("Failed to add items to basket"); 41 | } 42 | }, 1000); 43 | } 44 | 45 | async rollback() { 46 | // Implement rollback logic if necessary 47 | console.log("Rolling back basket"); 48 | } 49 | } 50 | 51 | class OrderState { 52 | constructor(coordinator) { 53 | this.coordinator = coordinator; 54 | } 55 | 56 | async execute() { 57 | console.log("Creating order"); 58 | // Simulate async operation and potential failure 59 | setTimeout(async () => { 60 | try { 61 | this.coordinator.changeState(new PaymentState(this.coordinator)); 62 | await this.coordinator.start(this.coordinator.orderData); 63 | } catch (error) { 64 | throw new Error("Failed to create order"); 65 | } 66 | }, 1000); 67 | } 68 | 69 | async rollback() { 70 | console.log("Rolling back order"); 71 | // Implement rollback logic if necessary 72 | } 73 | } 74 | 75 | class PaymentState { 76 | constructor(coordinator) { 77 | this.coordinator = coordinator; 78 | } 79 | 80 | async execute() { 81 | console.log("Processing payment"); 82 | // Simulate async operation and potential failure 83 | setTimeout(async () => { 84 | try { 85 | this.coordinator.changeState(new ShippingState(this.coordinator)); 86 | await this.coordinator.start(this.coordinator.orderData); 87 | } catch (error) { 88 | throw new Error("Failed to process payment"); 89 | } 90 | }, 1000); 91 | } 92 | 93 | async rollback() { 94 | console.log("Rolling back payment"); 95 | // Implement rollback logic if necessary 96 | } 97 | } 98 | 99 | class ShippingState { 100 | constructor(coordinator) { 101 | this.coordinator = coordinator; 102 | } 103 | 104 | async execute() { 105 | console.log("Shipping order"); 106 | // Simulate async operation and potential failure 107 | setTimeout(async () => { 108 | try { 109 | this.coordinator.changeState(new NotificationState(this.coordinator)); 110 | await this.coordinator.start(this.coordinator.orderData); 111 | } catch (error) { 112 | throw new Error("Failed to ship order"); 113 | } 114 | }, 1000); 115 | } 116 | 117 | async rollback() { 118 | console.log("Rolling back shipping"); 119 | // Implement rollback logic if necessary 120 | } 121 | } 122 | 123 | class NotificationState { 124 | constructor(coordinator) { 125 | this.coordinator = coordinator; 126 | } 127 | 128 | async execute() { 129 | console.log("Sending notification"); 130 | // Simulate async operation and potential failure 131 | setTimeout(async () => { 132 | try { 133 | console.log("Saga completed successfully"); 134 | } catch (error) { 135 | throw new Error("Failed to send notification"); 136 | } 137 | }, 1000); 138 | } 139 | 140 | async rollback() { 141 | console.log("Rolling back notification"); 142 | // Implement rollback logic if necessary 143 | } 144 | } 145 | 146 | const coordinator = new SagaCoordinator(); 147 | coordinator 148 | .start({ userId: "user1", items: ["item1", "item2"] }) 149 | .catch((error) => console.error(error.message)); 150 | --------------------------------------------------------------------------------