Nguyên tắc SOLID trong lập trình là một tập hợp các nguyên tắc thiết kế phần mềm. Các nguyên tắc này giúp tạo ra mã nguồn linh hoạt, dễ bảo trì và mở rộng về sau. Vậy SOLID là gì cùng mình phân tích phía dưới nhé:
1. S - Single Responsibility Principle (Nguyên tắc đơn trách nhiệm)
- Nguyên tắc này nói rằng: Mỗi lớp nên chỉ có một lý do để thay đổi và chỉ thực hiện một chức năng cụ thể. Điều này giúp giữ cho mã nguồn dễ đọc, bảo trì và hiểu.
- Ví dụ:
class Employee { private String name; private double salary; // methods to get/set employee details
} class EmployeeDatabase { void saveEmployee(Employee emp) { // save employee to database }
}
Khi bạn có một lớp Employee, nó chỉ chịu trách nhiệm quản lý thông tin về một nhân viên cụ thể mà không thực hiện các tác vụ không liên quan như việc lưu trữ dữ liệu vào cơ sở dữ liệu. Lớp EmployeeDatabase mới là lớp thực hiện điều đó.
2. O - Open/Closed Principle (Nguyên tắc mở rộng)
- Nguyên tắc này là : Phần mềm nên mở rộng được mà không cần phải sửa đổi mã nguồn hiện tại. Mở rộng có thể được thực hiện thông qua kế thừa hoặc sử dụng interfaces, không làm thay đổi mã nguồn đã tồn tại.
- Ví dụ:
interface Shape { double area();
} class Circle implements Shape { private double radius; @Override public double area() { return Math.PI * radius * radius; }
} class Rectangle implements Shape { private double width; private double height; @Override public double area() { return width * height; }
}
Sử dụng kế thừa và interface để mở rộng chức năng của một hệ thống mà không cần sửa đổi mã nguồn hiện có.
3. L - Liskov Substitution Principle (Nguyên tắc thay thế Liskov)
- Các đối tượng của lớp con nên có thể thay thế được cho các đối tượng của lớp cha mà không làm thay đổi tính đúng đắn của chương trình. Điều này đảm bảo tính nhất quán khi thay thế các đối tượng.
- Ví dụ:
class Bird { void fly() { // logic for flying }
} class Sparrow extends Bird { // Sparrow can fly
} class Ostrich extends Bird { void fly() { throw new UnsupportedOperationException("Ostrich cannot fly"); }
}
Như chúng ta thấy con chim sẻ (Sparrow) là con chim có thể bay nhưng con đà điểu (Ostrich) là một họ nhà chim nhưng không thể bay được, nên khi chúng ta khai báo là:
Bird bird1 = new Sparrow();// gọi phương thức bird1.fly() thì bay được
Bird bird2 = new Ostrich();// còn gọi phương thức bird2.fly() thì không bay được @@
4. I - Interface Segregation Principle (Nguyên tắc tách biệt giao diện)
- Một lớp không nên bắt buộc triển khai các phương thức mà nó không sử dụng. Các interfaces nên được chia thành các interfaces nhỏ hơn để đảm bảo rằng lớp chỉ triển khai những gì cần thiết cho nhiệm vụ của mình.
- Ví dụ:
interface Printer { void print(); void scan();
}
// thay vì dùng 1 interface Printer thì tạo ra 2 interface riêng biết rồi triển khai cả 2 interface SimplePrinter { void print();
} interface Scanner { void scan();
}
Một cái máy in đơn giản thì chỉ nên có chức năng in thôi, chức năng quét thì máy có máy không :v
5. D - Dependency Inversion Principle (Nguyên tắc đảo ngược sự phụ thuộc)
- Các module cấp cao không nên phụ thuộc vào chi tiết cấp thấp. Cả hai nên phụ thuộc vào abstraction. Điều này giúp giảm độ ràng buộc và tạo ra hệ thống linh hoạt, có thể thay đổi cài đặt mà không ảnh hưởng đến module khác.
- Ví dụ:
interface DataAccess { void save();
} class Database implements DataAccess { @Override public void save() { // save to database }
} class Customer { private DataAccess dataAccess; public Customer(DataAccess dataAccess) { this.dataAccess = dataAccess; } public void save() { dataAccess.save(); }
}
Trong ví dụ trê, Customer không phụ thuộc trực tiếp vào Database, mà thay vào đó phụ thuộc vào một interface DataAccess.
Tóm tắt về SOLID
Tên | Định nghĩa |
---|---|
Nguyên tắc Đơn trách nhiệm (SRP) | Mỗi lớp nên chỉ có một lý do để thay đổi và chỉ thực hiện một chức năng cụ thể. Điều này giúp giữ cho mã nguồn dễ đọc, bảo trì và hiểu. |
Nguyên tắc Mở rộng (OCP) | Phần mềm nên mở rộng được mà không cần phải sửa đổi mã nguồn hiện tại. Mở rộng có thể được thực hiện thông qua kế thừa hoặc sử dụng interfaces, không làm thay đổi mã nguồn đã tồn tại. |
Nguyên tắc Thay đổi không ảnh hưởng đến Người dùng (LSP) | Các đối tượng của lớp con nên có thể thay thế được cho các đối tượng của lớp cha mà không làm thay đổi tính đúng đắn của chương trình. Điều này đảm bảo tính nhất quán khi thay thế các đối tượng. |
Nguyên tắc Tách Biệt (ISP) | Một lớp không nên bắt buộc triển khai các phương thức mà nó không sử dụng. Các interfaces nên được chia thành các interfaces nhỏ hơn để đảm bảo rằng lớp chỉ triển khai những gì cần thiết cho nhiệm vụ của mình. |
Nguyên tắc Phụ thuộc vào Abstraction, không phải Implementations (DIP) | Các module cấp cao không nên phụ thuộc vào chi tiết cấp thấp. Cả hai nên phụ thuộc vào abstraction. Điều này giúp giảm độ ràng buộc và tạo ra hệ thống linh hoạt, có thể thay đổi cài đặt mà không ảnh hưởng đến module khác. |
Lời kết
Thực ra phấn đấu chính là bình thản sống mỗi ngày, làm thật tốt những việc mình đang phải làm, không trì hoãn, không than phiền, không thoái thác, không lười biếng. Mỗi ngày cố gắng thêm một chút xíu, mới có thể gộp thành hàng ngàn dũng khí, mang theo sự kiên trì, dẫn bạn đến nơi bạn muốn đến. Chúc bạn năm mới thành công