Nguyên tắc SOLID trong lập trình OOP

Lập trình viên phần mềm và 5 nguyên tắc SOLID: Viết mã sạch, dễ bảo trì và mở rộng

Là một lập trình viên phần mềm, một trong những kỹ năng quan trọng nhất bạn có thể phát triển là viết ra mã sạch, dễ bảo trì và có khả năng mở rộng. Việc viết mã như vậy không chỉ giúp quản lý dự án dễ dàng hơn mà còn giúp hệ thống phát triển mà không tạo ra sự phức tạp không cần thiết.

Một phương pháp cực kỳ hiệu quả để đạt được điều này là tuân thủ theo nguyên tắc SOLID. SOLID là từ viết tắt của 5 nguyên tắc thiết kế giúp bạn viết và thiết kế mã tốt hơn. Những nguyên tắc này được giới thiệu bởi Robert C. Martin (thường được gọi là “Uncle Bob”), và đóng vai trò là kim chỉ nam trong thiết kế hướng đối tượng.


SOLID là gì?

SOLID là tập hợp 5 nguyên tắc giúp phần mềm trở nên dễ hiểu hơn, linh hoạt hơn và dễ bảo trì hơn:

  • SSingle Responsibility Principle
  • OOpen/Closed Principle
  • LLiskov Substitution Principle
  • IInterface Segregation Principle
  • DDependency Inversion Principle


1. Single Responsibility Principle (SRP)

Ý nghĩa: Một lớp chỉ nên có một lý do duy nhất để thay đổi, nghĩa là nó chỉ nên đảm nhận một trách nhiệm duy nhất.

Ví dụ thực tế: Hãy tưởng tượng một máy pha cà phê vừa pha cà phê, vừa in hóa đơn. Nếu chức năng in hóa đơn bị lỗi, bạn có thể phải sửa phần pha cà phê, dù hai chức năng không liên quan. Thay vào đó, chúng nên được tách thành hai lớp riêng biệt.

class CoffeeMachine {
    public void brewCoffee() {
        System.out.println("Đang pha cà phê");
    }
}

class ReceiptPrinter {
    public void printReceipt() {
        System.out.println("Đang in hóa đơn");
    }
}

public class Main {
    public static void main(String[] args) {
        CoffeeMachine coffeeMachine = new CoffeeMachine();
        ReceiptPrinter receiptPrinter = new ReceiptPrinter();

        coffeeMachine.brewCoffee();
        receiptPrinter.printReceipt();
    }
}

2. Open/Closed Principle (OCP)

Ý nghĩa: Một lớp nên mở để mở rộng nhưng đóng để sửa đổi. Bạn có thể thêm tính năng mới mà không cần chỉnh sửa mã gốc.

Ví dụ thực tế: Một lớp Vehicle ban đầu chỉ hỗ trợ Car. Sau này, bạn muốn thêm Bike, Truck… mà không sửa lớp Vehicle.

abstract class Vehicle {
    public abstract void move();
}

class Car extends Vehicle {
    public void move() {
        System.out.println("Ô tô đang di chuyển");
    }
}

class Bike extends Vehicle {
    public void move() {
        System.out.println("Xe đạp đang di chuyển");
    }
}

public class Main {
    public static void main(String[] args) {
        Vehicle car = new Car();
        Vehicle bike = new Bike();

        car.move();
        bike.move();
    }
}

3. Liskov Substitution Principle (LSP)

Ý nghĩa: Lớp con phải có thể thay thế lớp cha mà không làm thay đổi tính đúng đắn của chương trình.

Ví dụ thực tế: Giả sử bạn có lớp Bird với phương thức fly(). Nếu bạn tạo lớp Penguin kế thừa từ Bird, nhưng chim cánh cụt lại không bay được, thì việc có fly()Penguin sẽ sai.

class Bird {
    public void eat() {
        System.out.println("Đang ăn");
    }
}

class FlyingBird extends Bird {
    public void fly() {
        System.out.println("Đang bay");
    }
}

class Sparrow extends FlyingBird {
    public void fly() {
        System.out.println("Chim sẻ đang bay");
    }
}

class Penguin extends Bird {
    public void swim() {
        System.out.println("Chim cánh cụt đang bơi");
    }
}

public class Main {
    public static void main(String[] args) {
        Bird bird1 = new Sparrow();
        bird1.eat();
        ((FlyingBird) bird1).fly();

        Bird bird2 = new Penguin();
        bird2.eat();
        // Không gọi fly() vì Penguin không biết bay
    }
}

4. Interface Segregation Principle (ISP)

Ý nghĩa: Client không nên bị ép buộc phải phụ thuộc vào những phương thức mà nó không sử dụng.

Ví dụ thực tế: Một lớp Document với các phương thức in, scan, fax. Nếu một máy in chỉ cần in, thì không nên bị buộc phải triển khai cả scan()fax().

interface Printable {
    void printDocument();
}

interface Scannable {
    void scanDocument();
}

interface Faxable {
    void faxDocument();
}

class Printer implements Printable {
    public void printDocument() {
        System.out.println("Đang in tài liệu");
    }
}

class Scanner implements Scannable {
    public void scanDocument() {
        System.out.println("Đang quét tài liệu");
    }
}

public class Main {
    public static void main(String[] args) {
        Printer printer = new Printer();
        printer.printDocument();

        Scanner scanner = new Scanner();
        scanner.scanDocument();
    }
}

5. Dependency Inversion Principle (DIP)

Ý nghĩa: Các module cấp cao không nên phụ thuộc vào module cấp thấp; cả hai nên phụ thuộc vào abstraction (giao diện).

Ví dụ thực tế: Giả sử Car phụ thuộc vào GasEngine. Nếu chuyển sang ElectricEngine, bạn phải sửa mã lớp Car. Tốt hơn nên phụ thuộc vào một Engine interface để có thể hoán đổi linh hoạt.

interface Engine {
    void start();
}

class GasEngine implements Engine {
    public void start() {
        System.out.println("Khởi động động cơ xăng");
    }
}

class ElectricEngine implements Engine {
    public void start() {
        System.out.println("Khởi động động cơ điện");
    }
}

class Car {
    private Engine engine;

    public Car(Engine engine) {
        this.engine = engine;
    }

    public void start() {
        engine.start();
    }
}

public class Main {
    public static void main(String[] args) {
        Engine gasEngine = new GasEngine();
        Car gasCar = new Car(gasEngine);
        gasCar.start();

        Engine electricEngine = new ElectricEngine();
        Car electricCar = new Car(electricEngine);
        electricCar.start();
    }
}

Kết luận

Các nguyên tắc SOLID giúp bạn viết mã nguồn sạch, dễ mở rộng, dễ bảo trì và ít lỗi hơn. Khi áp dụng chúng trong các dự án Java hoặc bất kỳ ngôn ngữ hướng đối tượng nào, bạn sẽ tạo ra phần mềm linh hoạt và bền vững hơn.

Tóm tắt nhanh:

  • SRP – Một lớp chỉ nên có một trách nhiệm.
  • OCP – Mở rộng được mà không cần sửa đổi mã cũ.
  • LSP – Lớp con có thể thay thế lớp cha mà không phá vỡ chương trình.
  • ISP – Chỉ nên dùng những giao diện thực sự cần thiết.
  • DIP – Phụ thuộc vào abstraction chứ không phải implementation.

👉 Áp dụng đúng các nguyên tắc này sẽ giúp bạn trở thành một lập trình viên chuyên nghiệp hơn. Chúc bạn viết mã vui vẻ!

Bài dịch CHIA SẺ và HỌC TẬP

Nguồn: Mastering SOLID Principles in Java: A Beginner’s Guide to Writing Clean Code

Comments are closed.