[ Better ] SOLID Là Gì? Giới Thiệu Về 5 Nguyên Tắc Cơ Bản Trong Lập Trình

Avatar admin | June 23, 2024

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:

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:

[ Better ] SOLID Là Gì? Giới Thiệu Về 5 Nguyên Tắc Cơ Bản Trong Lập Trình
[ Better ] SOLID Là Gì? Giới Thiệu Về 5 Nguyên Tắc Cơ Bản Trong Lập Trình

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ư OrderRepositoryOrderNotification sẽ xử lý việc lưu trữ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ư PercentageDiscountFixedDiscount 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 OrderSaverOrderNotifier 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);


[ Better ] SOLID Là Gì? Giới Thiệu Về 5 Nguyên Tắc Cơ Bản Trong Lập Trình
[ Better ] SOLID Là Gì? Giới Thiệu Về 5 Nguyên Tắc Cơ Bản Trong Lập Trình

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.

Leave a Reply