Chào bạn, nhớ bài viết trước mình từng "tám" về độ phức tạp trong phần mềm không? Mình có một châm ngôn luôn tâm niệm thế này: "Mã mình 'đụng' vào phải dễ hiểu và dễ bảo trì hơn lúc ban đầu—trừ khi mình viết mới hoàn toàn." Nghe có vẻ "nghiêm túc" nhưng thực ra đây là kim chỉ nam giúp mình luôn giữ code sạch đẹp đó! <img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://i.imgur.com/clean_desk_code.png' alt='Bàn làm việc gọn gàng và code sạch sẽ'> Khi làm việc với OOP (Lập trình hướng đối tượng), mình luôn bắt đầu từ những đơn vị nhỏ nhất. Với một "dân Ruby" như mình, thì đó chính là các **hàm** (function) đó bạn! Đối với mình, hàm chính là những viên gạch xây dựng cơ bản nhất của một lớp (class). Kể cả khi bạn có một lớp dài "một ngàn lẻ một dòng" đi chăng nữa, nếu các hàm bên trong nó rõ ràng và cấu trúc tốt, thì mọi thứ vẫn "chơi" được! Ít nhất là sau này muốn "đại tu" (refactor) cũng dễ thở hơn rất nhiều. Hôm nay, mình muốn bật mí 3 bí kíp "ruột" mà mình luôn áp dụng để viết ra những hàm vừa đẹp, vừa dễ bảo trì. Chuẩn bị sổ bút nha! <img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://i.imgur.com/coding_principles.png' alt='Các nguyên tắc viết code'> ### 1️⃣ Hàm ơi, "ngắn thôi đừng dài"! (Số dòng code) Ở Ruby, việc giữ cho các hàm ngắn gọn là một "nét văn hóa" rồi đó bạn. Hồi mình còn làm Trưởng nhóm kỹ thuật, mình hay đặt ra một "giới hạn mềm" là 10-12 dòng code cho một hàm. Nhưng mà, tùy vào "level" của đội ngũ mà con số này có thể hơi "khó nhằn" và làm chậm tiến độ. Sau nhiều phen "thử và sai", mình tìm ra một giải pháp "hòa bình" hơn: **20 dòng code là một mức tối đa hợp lý.** Con số này giúp hàm của bạn luôn đơn giản, dễ đọc và dễ bảo trì, mà lại không gây ra "áp lực" không cần thiết cho cả team. Cứ như một câu chuyện mini vậy, mỗi hàm chỉ kể một phần nhỏ thôi! <img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://i.imgur.com/short_code.png' alt='Mã nguồn ngắn gọn'> #### 📌 Ví dụ: "Trước và Sau" khi cắt tỉa Cùng xem một ví dụ "kinh điển" nhé! **Trước đây:** Một hàm dài "lê thê" và khó hiểu, cứ trộn lẫn đủ thứ công việc với nhau. Bạn xem này, nó giống như một "siêu nhân" gánh vác mọi nhiệm vụ cùng lúc vậy: ```ruby def process_payment(user, order_id) order = Order.find(order_id) raise "Order not found" unless order payment_gateway = PaymentGateway.new(user.payment_token) payment_result = payment_gateway.charge(order.amount_cents) if payment_result.success? order.update!(status: :paid, paid_at: Time.current) AnalyticsLogger.log_payment_success(user.id, order.id) NotificationService.send_payment_confirmation_email(user, order) if user.referral_code.present? reward_service = ReferralRewardService.new(user.referral_code) reward_service.process_reward_for(user) end SendThankYouGiftJob.perform_later(user.id) if order.amount_cents > 100_000 else order.update!(status: :payment_failed) ErrorTracker.notify("Payment failed", user_id: user.id, order_id: order.id) end end ``` **Và đây là Sau khi "biến hình":** Hàm đã được "tách lớp" rõ ràng, mỗi hàm chỉ tập trung làm một việc duy nhất. Giờ thì "siêu nhân" đã trở thành một "biệt đội chuyên gia", mỗi người một nhiệm vụ, phối hợp cực ăn ý! ```ruby def process_payment(user, order_id) order = Order.find(order_id) raise "Order not found" unless order if charge_order(user, order) handle_successful_payment(user, order) else handle_failed_payment(user, order) end end def charge_order(user, order) result = PaymentGateway.new(user.payment_token).charge(order.amount_cents) return false unless result.success? order.update!(status: :paid, paid_at: Time.current) true end def handle_successful_payment(user, order) log_payment_success(user, order) notify_user_of_payment(user, order) reward_referral_if_applicable(user) send_thank_you_gift_if_high_value(user, order) end def handle_failed_payment(user, order) order.update!(status: :payment_failed) ErrorTracker.notify("Payment failed", user_id: user.id, order_id: order.id) end def log_payment_success(user, order) AnalyticsLogger.log_payment_success(user_id: user.id, order_id: order.id) end def notify_user_of_payment(user, order) NotificationService.send_payment_confirmation_email(user, order) end def reward_referral_if_applicable(user) return unless user.referral_code.present? reward_service = ReferralRewardService.new(user.referral_code) reward_service.process_reward_for(user) end def send_thank_you_gift_if_high_value(user, order) return unless order.amount_cents > 100_000 SendThankYouGiftJob.perform_later(user.id) end ``` <img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://i.imgur.com/refactor_flow.png' alt='Sơ đồ refactoring của hàm process_payment'> ### 2️⃣ Một nhiệm vụ, một lý do để thay đổi (Single Responsibility Principle) Nguyên tắc này nói rằng: **Một hàm chỉ nên có MỘT lý do duy nhất để thay đổi.** Nghe có vẻ "trừu tượng" nhưng thực ra nó chính là anh em song sinh với Nguyên tắc Đơn nhiệm (Single Responsibility Principle - SRP) đó bạn. Để kiểm tra nhanh gọn lẹ, bạn cứ tự hỏi mình: "Cái hàm này làm gì?" Nếu câu trả lời của bạn có từ "và" (ví dụ: "Nó xử lý thanh toán **và** gửi email xác nhận"), thì xin chia buồn, hàm của bạn đang ôm đồm quá nhiều việc rồi đấy! Nó giống như một đầu bếp vừa nấu ăn, vừa lau dọn, vừa phục vụ bàn vậy – mỗi khi có yêu cầu mới, anh ta sẽ rất bận rộn và dễ sai sót. Trong ví dụ `process_payment` ban đầu, hàm này có "ti tỉ" lý do để thay đổi: logic thanh toán, ghi log, gửi thông báo, xử lý thưởng giới thiệu, hay cả các tác vụ chạy nền (background jobs). Giờ đây, mỗi hàm con chỉ tập trung vào duy nhất một nhiệm vụ, việc thay đổi trở nên dễ dàng và ít rủi ro hơn rất nhiều. Sướng! <img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://i.imgur.com/single_responsibility.png' alt='Mỗi người một việc, mỗi hàm một nhiệm vụ'> ### 3️⃣ Cùng "tầm nhìn", cùng "chiều cao" (Consistent Level of Abstraction) Nguyên tắc này nghe có vẻ "triết lý" nhưng lại cực kỳ hiệu quả đó nha! Nếu một hàm cứ "nhảy cóc" giữa các cấp độ trừu tượng khác nhau, nó sẽ làm bạn "mệt não" khi cố gắng hiểu nó hoạt động thế nào. Tưởng tượng bạn đang đọc một câu chuyện, mà tác giả cứ lúc thì mô tả tổng quan cả thành phố, lúc lại đi sâu vào chi tiết cái lông mày của một nhân vật. Khó theo dõi đúng không? Trong phiên bản hàm `process_payment` "Before", hàm của chúng ta cứ "nhảy múa" đủ mọi cấp độ: * Truy cập database (cấp độ thấp - chi tiết) * Gọi API thanh toán bên ngoài (hạ tầng - chi tiết) * Áp dụng logic nghiệp vụ (cấp độ trung bình - tổng quan hơn) * Gửi thông báo (tương tác đầu vào/đầu ra - chi tiết) * Kích hoạt các tác vụ nền (hạ tầng - chi tiết) Đó là cả một "mớ bòng bong" các ngữ cảnh, khiến bạn phải chuyển đổi tư duy liên tục! Nhưng ở phiên bản "After", hàm chính `process_payment` chỉ đứng ở cấp độ "điều phối" (orchestration). Nó giống như một "chỉ huy" giao việc cho từng "chuyên gia" (các hàm con). Mỗi hàm phụ lại giữ nguyên một "độ cao" nhất định, khiến toàn bộ luồng xử lý trở nên dễ theo dõi và phát triển hơn rất nhiều. Cứ như bạn đang xem một bản đồ có nhiều lớp vậy, mỗi lớp hiển thị một loại thông tin ở một cấp độ chi tiết nhất định. <img src='https://truyentranh.letranglan.top/api/v1/proxy?url=https://i.imgur.com/abstraction_levels.png' alt='Các cấp độ trừu tượng trong lập trình'> Đó là 3 bí kíp "xịn xò" mà mình muốn chia sẻ với bạn. Bạn có "chiêu" nào hay ho để giữ cho hàm của mình luôn "sạch sẽ" và dễ bảo trì không? Comment xuống dưới để chúng ta cùng "tám" nhé! 👇
Tìm hiểu 3 nguyên tắc vàng giúp bạn viết hàm (function) gọn gàng, dễ hiểu và dễ bảo trì trong lập trình, từ việc giữ code ngắn gọn đến áp dụng Nguyên tắc Trách nhiệm Duy nhất và cấp độ trừu tượng nhất quán.