Quá trình phỏng vấn với những câu hỏi lập trình có sự đa dạng lớn, SOLID là một trong những chủ đề quan trọng mà mình đã được hỏi. Vậy SOLID là gì và tại sao nó lại quan trọng đối với lập trình viên? Hãy cùng khám phá bài viết dưới đây!
SOLID là một tập hợp năm nguyên tắc thiết kế phần mềm dành cho lập trình hướng đối tượng (OOP). Những nguyên tắc này giúp cải thiện khả năng mở rộng, dễ bảo trì và nâng cao khả năng đọc mã nguồn. Dưới đây là chi tiết từng nguyên tắc kèm theo ví dụ minh họa bằng TypeScript.
1. Nguyên Tắc Trách Nhiệm Đơn (Single Responsibility Principle - SRP)
Khái niệm: Mỗi lớp (class) trong lập trình cần chỉ đảm nhận một trách nhiệm duy nhất.
Ví dụ:
typescript
// Vi phạm SRP
class UserManager {
createUser(user: string) {
console.log(`Người dùng ${user} đã được tạo`);
}
logActivity(activity: string) {
console.log(`Hoạt động đã được ghi lại: ${activity}`);
}
}
// Tuân thủ SRP
class UserCreator {
createUser(user: string) {
console.log(`Người dùng ${user} đã được tạo`);
}
}
class ActivityLogger {
logActivity(activity: string) {
console.log(`Hoạt động đã được ghi lại: ${activity}`);
}
}
// Sử dụng
const userCreator = new UserCreator();
const logger = new ActivityLogger();
userCreator.createUser("John");
logger.logActivity("Tạo người dùng");
2. Nguyên Tắc Mở Đóng (Open/Closed Principle - OCP)
Khái niệm: Một lớp nên có thể mở rộng mà không cần phải thay đổi mã nguồn đã có.
Ví dụ:
typescript
// Vi phạm OCP
class Notification {
send(type: string, message: string) {
if (type === "email") {
console.log(`Gửi email: ${message}`);
} else if (type === "sms") {
console.log(`Gửi SMS: ${message}`);
}
}
}
// Tuân thủ OCP
interface Notification {
send(message: string): void;
}
class EmailNotification implements Notification {
send(message: string): void {
console.log(`Gửi email: ${message}`);
}
}
class SMSNotification implements Notification {
send(message: string): void {
console.log(`Gửi SMS: ${message}`);
}
}
// Sử dụng
function notify(notification: Notification, message: string) {
notification.send(message);
}
notify(new EmailNotification(), "Xin chào qua Email!");
notify(new SMSNotification(), "Xin chào qua SMS!");
3. Nguyên Tắc Thay Thế Liskov (Liskov Substitution Principle - LSP)
Khái niệm: Các lớp con cần có khả năng thay thế lớp cha mà không làm thay đổi hành vi của chương trình.
Ví dụ:
typescript
// Vi phạm LSP
class Bird {
fly() {
console.log("Đang bay");
}
}
class Penguin extends Bird {
fly() {
throw new Error("Chim cánh cụt không thể bay");
}
}
// Tuân thủ LSP
class Bird {
move() {
console.log("Đang di chuyển");
}
}
class FlyingBird extends Bird {
move() {
console.log("Đang bay");
}
}
class Penguin extends Bird {
move() {
console.log("Đang bơi");
}
}
// Sử dụng
const birds: Bird[] = [new FlyingBird(), new Penguin()];
birds.forEach((bird) => bird.move());
4. Nguyên Tắc Phân Tách Giao Diện (Interface Segregation Principle - ISP)
Khái niệm: Một giao diện không nên épCác lớp thực thi phải phụ thuộc vào các phương thức không cần thiết.
Ví dụ:
typescript
// Vi phạm ISP
interface Animal {
fly(): void;
swim(): void;
}
class Dog implements Animal {
fly() {
throw new Error("Chó không thể bay");
}
swim() {
console.log("Chó đang bơi");
}
}
// Tuân thủ ISP
interface Swimmer {
swim(): void;
}
interface Flyer {
fly(): void;
}
class Dog implements Swimmer {
swim() {
console.log("Chó đang bơi");
}
}
class Bird implements Flyer {
fly() {
console.log("Chim đang bay");
}
}
// Sử dụng
const dog = new Dog();
dog.swim();
const bird = new Bird();
bird.fly();
5. Nguyên Tắc Đảo Ngược Phụ Thuộc (Dependency Inversion Principle - DIP)
Khái niệm: 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 trừu tượng (abstraction).
Ví dụ:
typescript
// Vi phạm DIP
class MySQLDatabase {
connect() {
console.log("Kết nối đến MySQL");
}
}
class App {
db: MySQLDatabase;
constructor() {
this.db = new MySQLDatabase();
}
start() {
this.db.connect();
}
}
// Tuân thủ DIP
interface Database {
connect(): void;
}
class MySQLDatabase implements Database {
connect() {
console.log("Kết nối đến MySQL");
}
}
class MongoDBDatabase implements Database {
connect() {
console.log("Kết nối đến MongoDB");
}
}
class App {
db: Database;
constructor(db: Database) {
this.db = db;
}
start() {
this.db.connect();
}
}
// Sử dụng
const app1 = new App(new MySQLDatabase());
app1.start();
const app2 = new App(new MongoDBDatabase());
app2.start();
Các nguyên tắc SOLID đóng vai trò rất quan trọng trong việc thiết kế mã nguồn rõ ràng, dễ bảo trì và mở rộng. Việc tuân thủ những nguyên tắc này không chỉ giúp giảm thiểu lỗi phát sinh khi thay đổi hoặc nâng cấp hệ thống mà còn làm tăng tính khả dụng của ứng dụng.
Tài Liệu Tham Khảo
- Bạn có thể tham khảo thêm thông tin tại blog của mình hoặc xem qua repository trên Github để thực hành các nguyên tắc SOLID với TypeScript. Hãy theo dõi các bài viết khác của mình để nâng cao kỹ năng lập trình!
#typescript #oop #solid
source: viblo