DDevArchive
Đăng nhập

SOLID, DRY, KISS: code sạch không phải code đẹp

Code sạch là code người khác (và bạn 6 tháng sau) đọc được mà không cần hỏi. SOLID là 5 nguyên tắc giúp code dễ thay đổi — và "dễ thay đổi" mới là thước đo duy nhất.

Bắt đầu từ KISS và DRY

KISS (Keep It Simple, Stupid): giải pháp đơn giản nhất hoạt động được. DRY (Don’t Repeat Yourself): một logic chỉ tồn tại một nơi. Đừng sinh ra “thư viện” cho thứ mới dùng một lần.

DRY quá đà cũng là bệnh

Gộp 2 chỗ giống nhau 50% thành một hàm “linh hoạt” với 4 tham số boolean — rồi sau này ai gọi cũng phải đọc hàm để hiểu. DRY áp cho ý nghĩa logic, không phải cho khuôn dạng code.

SOLID: 5 nguyên tắc kiến trúc

Nguyên tắcÝ nghĩaDấu hiệu vi phạm
S — Single ResponsibilityMỗi class/hàm có đúng một lý do để thay đổiHàm vừa gọi API vừa render UI vừa lưu DB
O — Open/ClosedMở cho mở rộng, đóng cho sửa đổiMỗi tính năng mới lại sửa hàm cũ bằng if
L — LiskovClass con thay thế được class cha mà không phá vỡOverrride method ném lỗi bất ngờ
I — Interface SegregationĐừng bắt class phụ thuộc method nó không dùngInterface có 10 method nhưng chỉ dùng 2
D — Dependency InversionPhụ thuộc abstraction, không phụ thuộc implementationHàm new Redis trực tiếp thay vì nhận qua tham số

S và D — hai cái bạn gặp hằng ngày

Single Responsibility: tách controller (nhận request), service (logic), repository (truy cập data). Dependency Inversion: truyền dependency vào qua tham số/constructor thay vì tự new bên trong — đó chính là nền của việc test (bạn sẽ thay DB thật bằng mock).

// File: before.js
// Một hàm làm quá nhiều việc, khó test
export async function registerUser(email, password) {
  const redis = new Redis();          // tự new, khó thay thế
  const user = await User.create({ email, password });
  await redis.set("user:" + user.id, JSON.stringify(user));
  await mailer.sendWelcome(email);    // gọi service bên trong
  return user;
}
// File: after.js
// Nhận dependency qua tham số -> thay được, test được
export function registerUser(userRepo, cache, mailer) {
  return async function (email, password) {
    const user = await userRepo.create({ email, password });
    await cache.set("user:" + user.id, JSON.stringify(user));
    await mailer.sendWelcome(email);
    return user;
  };
}
// Trong test: truyền fake cache, fake mailer vào

Thước đo đơn giản: 3 câu hỏi

Khi review code, hỏi: (1) Tôi có hiểu hàm này làm gì trong 10 giây không? (2) Nếu thay đổi một yêu cầu, tôi sửa ít chỗ nhất có thể chứ? (3) Tôi có thể test nó mà không cần khởi động cả hệ thống không?

❓ Dấu hiệu rõ nhất của vi phạm Single Responsibility?

  • Áp dụng KISS và DRY có chừng mực
  • Thuộc 5 chữ cái của SOLID
  • Truyền dependency qua tham số để test được
  • Review code bằng 3 câu hỏi nhanh