Nguyen Le PhongNguyen Le Phong

Các tầng trừu tượng trong lập trình

Mỗi tầng trừu tượng là một ván cược: thắng, các thay đổi xếp chồng lên dễ dàng; thua, bạn ôm một abstraction non và mọi thay đổi đặt lên nó đều bốc mùi. Cảm giác thỏa mãn khi đặt tên được mọi thứ là có thật — và chính điều đó khiến nó nguy hiểm. Khái quát hóa code chuyên biệt luôn dễ hơn gỡ rối một thiết kế trừu tượng quá đà.

Có một câu sáo ngữ rằng mọi thứ đều là đánh đổi, và trừu tượng hóa không phải ngoại lệ. Cách phổ biến nhất người ta với tay tới nó trong thiết kế OOP là trừu tượng hóa bất cứ thứ gì đặt tên được: “mình sẽ cần gửi một message — thêm một message service thôi; à, thêm cả message factory; thêm message dispatcher nữa; rồi còn…” — cứ thế mãi. Đôi khi nó vận hành tuyệt đẹp. Đôi khi nó lặng lẽ mang về nhiều mặt trái hơn giá trị của nó.

Mỗi abstraction là một ván cược

Nếu bạn thắng, việc đặt các thay đổi tiếp theo lên trên trở nên dễ và nhanh. Nếu bạn thua, giờ bạn sở hữu một abstraction non, và mọi thay đổi xếp lên trên đều ra mùi khó chịu. Rốt cuộc, khi thay đổi chồng lên thay đổi, nó bắt đầu giống một ván Jenga mà ai cũng biết trước hồi kết.

Vậy nên trừu tượng hóa không miễn phí. Đặt tên được mọi thứ, áp dụng được một design pattern mới học — cảm giác ấy dễ chịu thật, và chính điều đó khiến nó nguy hiểm. Nó là một canh bạc, và thường thì kèo nghiêng về phía thua. Phần mềm càng rối rắm sẵn, phần thắng của bạn càng co lại.

Chi phí đọc hiểu

Dù tên một abstraction có đẹp về ngữ nghĩa tới đâu, nó nhiều khả năng vẫn khó hiểu hơn phiên bản trực tiếp tương đương. Mỗi tầng trung gian người đọc phải băng qua lại rút thêm một chút nhận thức — nhất là với những người không chia sẻ mức thân thuộc của bạn với phần hiện thực. Và “người đọc” rất có thể là chính bạn, vài tháng sau, trân trân nhìn sự khôn khéo của mình mà thiếu mất cái ngữ cảnh từng khiến nó hiển nhiên.

Đây mới là điều K.I.S.S. thật sự nói: giữ mọi thứ trực tiếp đến mức “ngốc nghếch”, để bất kỳ ai cũng có thể Ctrl/Cmd+Click và mô phỏng được runtime sẽ diễn tiến ra sao — mà không phải giữ cả một tòa tháp gián tiếp trong đầu.

Hãy nhớ bạn luôn có thể quay lại trừu tượng hóa sau, nếu tính năng thật sự tiến hóa tới mức cần nó. Và tới lúc đó bạn sẽ hiểu vấn đề rõ hơn, sau vài lần được thực tế kiểm chứng với đoạn code hiện tại. Khái quát hóa code chuyên biệt luôn dễ hơn đi chiều ngược lại — gỡ rối một thiết kế quá kỹ thuật, quá trừu tượng, khi đã có những phần phụ thuộc xây bên trên.

Một codebase là một thực thể sống, có vòng đời và cách tiến hóa riêng; nó không phải sản phẩm giao một lần là xong. Hãy giữ trong đầu rủi ro trừu tượng saichi phí đọc hiểu khi biện giải các quyết định thiết kế — của bạn hay của người khác — và khi review code. (Một mạch tư duy được mài sắc cho tôi trong khu vườn suy nghĩ của một người bạn.)

Đọc thêm

Bạn thấy bài này thế nào?