0
0
Lập trình
TT

Áp dụng Nguyên tắc SOLID để Xây dựng Ứng dụng JavaScript và TypeScript Bền Vững

Đăng vào 2 tuần trước

• 4 phút đọc

Nguyên tắc SOLID: Nền tảng Chắc chắn cho Ứng dụng JavaScript và TypeScript

Nguyên tắc SOLID là bộ nguyên tắc quan trọng trong phát triển phần mềm, giúp tạo ra code rõ ràng, dễ bảo trì và có khả năng mở rộng. Bài viết này sẽ cung cấp một cái nhìn sâu sắc về từng nguyên tắc SOLID cùng với các ví dụ minh họa trong JavaScript (JS) và TypeScript (TS).

1. Nguyên tắc Trách nhiệm Duy nhất (SRP) - Single Responsibility Principle

Mô tả: Mỗi lớp hoặc module chỉ nên có một lý do để thay đổi, tức là chỉ nên đảm nhận một trách nhiệm duy nhất.

Ví dụ trong JavaScript (React)

Trong React, các component thường bị lạm dụng khi chúng làm quá nhiều việc khác nhau. Dưới đây là ví dụ về một component vi phạm nguyên tắc SRP:

javascript Copy
function UserProfile({ userId }) {
  const [user, setUser] = useState(null);

  useEffect(() => {
    fetchUserData();
  }, [userId]);

  async function fetchUserData() {
    const response = await fetch(`/api/users/${userId}`);
    const data = await response.json();
    setUser(data);
  }

  return <div>{user?.name}</div>;
}

Trong trường hợp này, component UserProfile đang xử lý việc render UI và lấy dữ liệu cùng một lúc.

Giải pháp: Sử dụng custom hook để tách logic lấy dữ liệu ra khỏi component UI:

javascript Copy
function useUserData(userId) {
  const [user, setUser] = useState(null);

  useEffect(() => {
    async function fetchUserData() {
      const response = await fetch(`/api/users/${userId}`);
      const data = await response.json();
      setUser(data);
    }
    fetchUserData();
  }, [userId]);

  return user;
}

function UserProfile({ userId }) {
  const user = useUserData(userId);
  return <div>{user?.name}</div>;
}

Ví dụ trong TypeScript (Angular)

Trong Angular, nếu một service có nhiều trách nhiệm, nó có thể trở nên lộn xộn:

typescript Copy
@Injectable()
export class UserService {
  constructor(private http: HttpClient) {}

  getUser(userId: string) {
    return this.http.get(`/api/users/${userId}`);
  }

  updateUserProfile(userId: string, data: any) {
    return this.http.put(`/api/users/${userId}`, data).subscribe(() => {
      console.log('User updated');
      alert('Profile updated successfully');
    });
  }
}

Ở đây, UserService có nhiều trách nhiệm, từ lấy dữ liệu đến cập nhật và xử lý thông báo.

Giải pháp: Tách việc xử lý thông báo thành một service riêng biệt:

typescript Copy
@Injectable()
export class UserService {
  ... 
}

@Injectable()
export class NotificationService {
  notify(message: string) {
    alert(message);
  }
}

2. Nguyên tắc Mở/Đóng (OCP) - Open/Closed Principle

Mô tả: Các thực thể phần mềm nên mở để mở rộng nhưng đóng để sửa đổi.

Ví dụ trong JavaScript (React)

Trong JavaScript, việc thêm logic xác thực mới vào một hàm có thể vi phạm nguyên tắc OCP:

javascript Copy
function validate(input) {
  ...
}

Giải pháp: Tạo các quy tắc xác thực riêng biệt.

javascript Copy
function validate(input, rules) {
  return rules.map(rule => rule(input)).find(result => result !== 'Valid') || 'Valid input';
}

Ví dụ trong TypeScript (Angular)

Dưới đây là một service vi phạm OCP:

typescript Copy
class NotificationService {
  send(type: 'email' | 'sms', message: string) {
    ...
  }
}

Giải pháp: Tạo các lớp notification riêng biệt cho từng loại thông báo.

typescript Copy
interface Notification {
  send(message: string): void;
}

3. Nguyên tắc Thay thế Liskov (LSP) - Liskov Substitution Principle

Mô tả: Các kiểu con phải có thể thay thế cho các kiểu cơ sở mà không làm hỏng chương trình.

Ví dụ trong JavaScript (React)

Sử dụng higher-order component (HOC) có thể gây nhầm lẫn. Đã đến lúc cần một giải pháp phù hợp hơn:

javascript Copy
function Clickable({ children, onClick }) {
  return <div onClick={onClick}>{children}</div>;
}

Ví dụ trong TypeScript (Angular)

Một ví dụ về việc vi phạm nguyên tắc LSP:

typescript Copy
class Rectangle { ... }
class Square extends Rectangle { ... }

Giải pháp: Tạo một lớp cơ sở để kế thừa cho cả RectangleSquare mà không làm hỏng tính chính xác của program.

4. Nguyên tắc Phân tách Giao diện (ISP) - Interface Segregation Principle

Mô tả: Clients không nên bị ép buộc phải phụ thuộc vào các giao diện mà chúng không sử dụng.

Ví dụ trong JavaScript (React)

Đảm bảo rằng các props chỉ đưa ra những gì được dùng.

javascript Copy
function UserProfileComponent({ user }) { ... }

Ví dụ trong TypeScript (Angular)

Tạo các interface tách biệt cho từng loại worker.

typescript Copy
interface Worker { ... }
interface Eater { ... }

5. Nguyên tắc Đảo ngược Phụ thuộc (DIP) - Dependency Inversion Principle

Mô tả: 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 các lớp trừu tượng.

Ví dụ trong JavaScript (React)

Các component nên có thể nhận input từ bên ngoài để tránh việc bị gắn chặt:

javascript Copy
function UserComponent({ userId, fetchUserData }) { ... }

Ví dụ trong TypeScript (Angular)

Bằng cách sử dụng interfaces, các component được tách ra khỏi các service cụ thể:

typescript Copy
interface UserService { ... }

Kết luận

Việc áp dụng các nguyên tắc SOLID trong phát triển phần mềm không chỉ giúp code của bạn trở nên rõ ràng, dễ sửa đổi mà còn dễ mở rộng để thích nghi với sự thay đổi trong tương lai. Khi kết hợp các nguyên tắc này vào dự án của bạn, bạn sẽ tạo ra các ứng dụng JavaScript và TypeScript bền vững và hiệu quả hơn.

source: viblo

Gợi ý câu hỏi phỏng vấn
Không có dữ liệu

Không có dữ liệu

Bài viết được đề xuất
Bài viết cùng tác giả

Bình luận

Chưa có bình luận nào

Chưa có bình luận nào