[ Better ] SOLID Là Gì? Giới Thiệu Về 5 Nguyên Tắc Cơ Bản Trong Lập Trình
Nguyên tắc cơ bản SOLID là một tập hợp các nguyên tắc thiết kế phần mềm giúp tạo ra mã nguồn có thể tái sử dụng, dễ bảo trì và mở rộng.
Viết tắt của năm nguyên tắc được gọi là SOLID:
Table of Contents
SOLID gồm 5 nguyên tắt cơ bản
Nguyên tắc trách nhiệm duy nhất
Nguyên tắc mở/đóng
Nguyên tắc Thay thế của Liskov
Nguyên tắc tách biệt giao diện
Nguyên tắc đảo ngược sự phụ thuộc
Dưới đây là một ví dụ cho mỗi nguyên tắc JavaScript:
Single Responsibility Principle (SRP)
Một nhóm chỉ nên có một trách nhiệm duy nhất, nghĩa là chỉ có một lý do để thay đổi.
class User { constructor(name, email) { this.name = name; this.email = email; } // Trách nhiệm của lớp này chỉ là quản lý thông tin người dùng } class UserRepository { save(user) { // Lưu người dùng vào cơ sở dữ liệu } delete(user) { // Xóa người dùng từ cơ sở dữ liệu } // Trách nhiệm của lớp này chỉ là tương tác với cơ sở dữ liệu }
Open/Closed Principle (OCP)
Các thực thể phần mềm như các lớp, module, chức năng, v.
v.
nên đóng cho sửa đổi nhưng mở cho mở rộng.
class Shape { area() { throw new Error("Phương thức này phải được cài đặt"); } } class Rectangle extends Shape { constructor(width, height) { super(); this.width = width; this.height = height; } area() { return this.width * this.height; } } class Circle extends Shape { constructor(radius) { super(); this.radius = radius; } area() { return Math.PI * Math.pow(this.radius, 2); } }
Liskov Substitution Principle (LSP)
Các đối tượng trong lớp con có thể được sử dụng để thay thế các đối tượng trong lớp cha mà không ảnh hưởng đến tính đúng đắn của chương trình.
class Bird { fly() { console.log("Bay trên trời!"); } } class Duck extends Bird { quack() { console.log("Quạc quạc!"); } } class Penguin extends Bird { fly() { throw new Error("Penguin không thể bay!"); } }
Vì lớp Penguin không thể thay thế hoàn toàn lớp Bird, nên nó vi phạm nguyên tắc LSP.
Interface Segregation Principle (ISP)
Các client không nên bị buộc phải phụ thuộc vào các giao diện mà họ không sử dụng.
class Printer { print(doc) { throw new Error("Phương thức này phải được cài đặt"); } } class Scanner { scan(doc) { throw new Error("Phương thức này phải được cài đặt"); } } class MultiFunctionPrinter { print(doc) { // Cài đặt logic in ấn } scan(doc) { // Cài đặt logic quét } } class SimplePrinter extends Printer { print(doc) { // Cài đặt logic in ấn } } // SimplePrinter không cần phải cài đặt phương thức scan
Dependency Inversion Principle (DIP)
Các module cấp cao không nên phụ thuộc vào các module cấp thấp.
Cả hai nên phụ thuộc vào các abstraction.
class Database { save(data) { throw new Error("Phương thức này phải được cài đặt"); } } class MySQLDatabase extends Database { save(data) { // Logic lưu trữ vào MySQL } } class UserService { constructor(database) { this.database = database; } saveUser(user) { this.database.save(user); } } const mySQLDatabase = new MySQLDatabase(); const userService = new UserService(mySQLDatabase); userService.saveUser({ name: "John Doe", email: "[email protected]" });
Đây là một minh họa cơ bản của nguyên tắc SOLID trong JavaScript.
Các nguyên tắc này giúp mở rộng, bảo trì và tái sử dụng mã nguồn.
Ví dụ thực tế áp dụng vào class Order
Single Responsibility Principle (SRP)
Nguyên tắc: Một class chỉ nên có một lý do duy nhất để thay đổi.
Giải thích: Class Order
chỉ nên chứa logic liên quan đến đơn đặt hàng, trong khi các lớp khác như OrderRepository
và OrderNotification
sẽ xử lý việc lưu trữ và thông báo đơn đặt hàng.
// Lớp Order chỉ chịu trách nhiệm quản lý thông tin đơn hàng class Order { constructor(orderId, items) { this.orderId = orderId; this.items = items; } calculateTotal() { return this.items.reduce((total, item) => total + item.price, 0); } } // Lớp OrderRepository chịu trách nhiệm lưu trữ đơn hàng class OrderRepository { save(order) { // Logic lưu đơn hàng vào cơ sở dữ liệu console.log(`Order ${order.orderId} has been saved.`); } } // Lớp OrderNotification chịu trách nhiệm gửi thông báo class OrderNotification { notify(order) { // Logic gửi thông báo về đơn hàng console.log(`Order ${order.orderId} notification has been sent.`); } }
Open/Closed Principle (OCP)
Nguyên tắc: Class nên mở cho việc mở rộng nhưng đóng cho việc sửa đổi.
Giải thích: Chúng ta có thể thêm các loại giảm giá mới mà không cần sửa đổi class Order
.
class Discount { apply(order) { throw new Error("Phương thức này phải được cài đặt"); } } class PercentageDiscount extends Discount { constructor(percentage) { super(); this.percentage = percentage; } apply(order) { return order.calculateTotal() * (1 - this.percentage / 100); } } class FixedDiscount extends Discount { constructor(amount) { super(); this.amount = amount; } apply(order) { return order.calculateTotal() - this.amount; } }
Liskov Substitution Principle (LSP)
Nguyên tắc: Các đối tượng của một class con nên có thể thay thế cho các đối tượng của class cha mà không làm thay đổi tính đúng đắn của chương trình.
Giải thích: Các lớp con như PercentageDiscount
và FixedDiscount
có thể thay thế cho lớp cha Discount
mà không ảnh hưởng đến logic của chương trình.
function applyDiscount(order, discount) { return discount.apply(order); } const order = new Order(1, [{ name: 'Item 1', price: 100 }, { name: 'Item 2', price: 200 }]); const percentageDiscount = new PercentageDiscount(10); const fixedDiscount = new FixedDiscount(50); console.log(applyDiscount(order, percentageDiscount)); // Tính tổng với giảm giá phần trăm console.log(applyDiscount(order, fixedDiscount)); // Tính tổng với giảm giá cố định
Interface Segregation Principle (ISP)
Nguyên tắc: Các client không nên bị buộc phải phụ thuộc vào các giao diện mà họ không sử dụng.
Giải thích: Chúng ta tách biệt các giao diện để các class chỉ cần cài đặt các phương thức mà chúng thực sự sử dụng.
class OrderSaver { save(order) { throw new Error("Phương thức này phải được cài đặt"); } } class OrderNotifier { notify(order) { throw new Error("Phương thức này phải được cài đặt"); } } class DatabaseOrderSaver extends OrderSaver { save(order) { console.log(`Order ${order.orderId} has been saved to the database.`); } } class EmailOrderNotifier extends OrderNotifier { notify(order) { console.log(`Order ${order.orderId} notification has been sent via email.`); } }
Dependency Inversion Principle (DIP)
Nguyên tắc: Các module cấp cao không nên phụ thuộc vào các module cấp thấp.
Cả hai nên phụ thuộc vào các abstraction.
Giải thích: OrderService
phụ thuộc vào abstraction OrderSaver
và OrderNotifier
thay vì phụ thuộc trực tiếp vào các class cụ thể.
class ChatController { constructor(chatService) { this.chatService = chatService; } handleNewMessage(message) { this.chatService.sendMessage(message); } } const simpleMessageSender = new SimpleMessageSender(); const inMemoryMessageSaver = new InMemoryMessageSaver(); const chatService = new ChatService(simpleMessageSender, inMemoryMessageSaver); const chatController = new ChatController(chatService); const user = new User(1, 'Alice'); const message = new Message(1, user, 'Hello, World!', new Date()); chatController.handleNewMessage(message);
Tóm tắt
SRP: Tách biệt các trách nhiệm (quản lý đơn hàng, lưu trữ, thông báo).
OCP: Mở rộng các loại giảm giá mà không sửa đổi lớp Order
.
LSP: Các lớp giảm giá con có thể thay thế lớp cha mà không ảnh hưởng đến logic chương trình.
ISP: Tách biệt các giao diện để tránh việc các class phải cài đặt các phương thức không cần thiết.
DIP: OrderService
phụ thuộc vào abstraction thay vì các lớp cụ thể, giúp dễ dàng thay thế và mở rộng.
#Mtips5s #Contact
Fanpage: https://www.facebook.com/mtipscoder
Group trao đổi, chia sẻ: https://www.facebook.com/groups/mtipscoder
Website: https://mtips5s.com
Youtube: https://mtips5s.com
Twitter(X): @takagiks99
Instagram: @khuongkara
Threads: @khuongkara
Google Maps: @khuongkara
#Base Code #Souce Code
Npm: @tools.mtips5s.com
Bộ công cụ My Self: @github
Npm: @npm
Docker: @docker
Chúc các bạn thành công!
Written by admin
Comments
This post currently has no responses.