Thiết Kế Module Tốt - nói dối e blog

Thiết Kế Module Tốt

Thiết kế mô-đun tốt
Chương trình tuần này khiến tôi vô cùng hào hứng - có hôm thức trắng đêm, hôm khác lại tan làm lúc 4 giờ sáng. Hai việc chính hoàn thành gồm nghiên cứu chiến lược mở rộng nền tảng tối ưu và xây dựng mô-đun quản lý tài nguyên.

Thành công thứ hai đã được hoàn thành vào hôm qua. Qua hành trình này, tôi tích lũy được nhiều kinh nghiệm quý giá đáng để chia sẻ - đặc biệt là về tiêu chuẩn thiết kế mô-đun xuất sắc. Đây là chủ đề rộng lớn, khiến tôi nhiều lần định tổng hợp suy nghĩ mà lại chùn tay.

Các bậc tiền bối đã viết nhiều về vấn đề này, trong khi trải nghiệm cá nhân khi chuyển thành văn bản thường mất đi nhiều tinh túy, dễ bị các chuyên gia chê cười. Chân lý đúng đắn nhất luôn đơn giản đến mức người ta thường lãng quên nó. Lập trình viên phải không ngừng viết code mới để đến một ngày bừng ngộ: Hóa ra những nguyên tắc tưởng chừng hiểu rõ từ lâu lại cần thời gian dài để thấu hiểu bản chất.

Đầu tiên: Tính trực giao (Orthogonality)
Chúng ta đều biết giữ tính trực giao giúp giảm thiểu lỗi trong thiết kế phức tạp. Trong thiết kế thuần túy, mọi thao tác nội bộ mô-đun không tạo ra hiệu ứng phụ bên ngoài.

Dù nguyên tắc đơn giản, hiện thực hóa lại không dễ dàng. Do đó chúng ta áp dụng các ràng buộc bổ sung để tiến gần hơn tới mục tiêu này.

Mô-đun của chúng ta có thể ở dạng .dll/.so thông thường (thuận tiện cho debug) hoặc định dạng tùy biến. Với định dạng tùy biến, không có bảng import/export - giao diện duy nhất là điểm bắt đầu. Trong giai đoạn phát triển, khi dùng module động hệ thống, chúng ta biên dịch với cờ –nostdlib (hoặc /nodefaultlib trên Windows) để loại bỏ liên kết ngầm với libc, đồng thời kiểm tra phụ thuộc ngoại vi.

Dù có vẻ khắt khe, đây là cách hiệu quả ngăn chặn hiệu ứng phụ. Điều này cũng khiến các thư viện bên thứ ba không dùng trực tiếp được - một trong những lý do khiến tôi liên tục phải tự xây dựng lại các thành phần cơ bản. Nguyên tắc này đã được duy trì nghiêm ngặt suốt 2 năm. Nhìn lại, thời gian bỏ ra tự xây dựng thực ra không nhiều, mà sau đó lại không bị lặp lại công việc. Khi có thành phần không phù hợp, việc thay thế cũng dễ dàng nhờ cam kết mạnh mẽ về tính trực giao.

Tiếp theo: Giao diện tối giản
Trong tác phẩm “Nghệ thuật lập trình Unix”, có trích dẫn câu nói nổi tiếng từ “Ngôn ngữ lập trình C”: “…Những giới hạn không chỉ khuyến khích tính tiết kiệm, mà còn góp phần tạo nên sự tinh tế trong thiết kế”. Để đạt được sự tối giản, ta không nên tập trung vào việc hệ điều hành/ngôn ngữ có thể làm được bao nhiêu, mà hãy suy nghĩ từ điểm ít nhất chúng có thể thực hiện - không hành động dựa trên giả định, mà bắt đầu từ con số 0.

Tôi đã đọc câu này nhiều lần qua các giai đoạn đời mình và luôn yêu thích. Tại sao API của Windows lại trông rối mắt? Vì nó cố gắng xử lý mọi tình huống, hầu như mọi nhóm API đều để ngỏ cửa mở rộng trong tương lai. Có những API từ khi sinh ra đến chết đi, tham số lpReserved cũng chưa từng được dùng tới.

Lúc còn là sinh viên, tôi say mê triết lý “vạn năng” của Windows: Xem này, chỉ cần bạn biết dùng, mọi tiềm năng của máy tính sẽ được khai thác. Dù chưa thể hiện thực hiện tại, giao diện đã mở ra viễn cảnh tương lai. Đáng tiếc, đến tận hôm nay tôi vẫn không thể fork một tiến trình trên Windows mà không ngán ngẩm với CreateProcess.

Thay đổi trong năm nay
Tôi bắt đầu dùng C để viết lại các đoạn C++ trong dự án. May mắn thay, phiên bản trước đã không dùng các tính năng cao cấp của C++, nên công việc này diễn ra suôn sẻ. Trong quá trình làm, tôi không ngừng suy nghĩ cách tinh gọn thiết kế, gạt bỏ hoặc hợp nhất các giao diện thừa.

Tôi yêu C chính vì những giới hạn của nó. Việc OO không tiện dẫn đến việc tôi hạn chế dùng OO. Không có lớp và không gian tên khiến tôi phải cân nhắc giảm số lượng hàm API. Khi quay lại C, tôi không dùng typedef cho tên cấu trúc nữa - union là union, struct là struct, tại sao phải đổi tên để tiết kiệm vài phím gõ? Thiết kế tối giản giúp giảm bớt lượng mã nguồn.

Dù muốn tránh bình luận sâu về OO hay ngôn ngữ OO cụ thể (tránh biến blog thành nơi hứng gạch), nhưng tác giả Eric S. Raymond đã có nhiều phân tích sâu sắc tại phụ lục 4.5 của “TAOUP”. Những người phản đối dù đọc kỹ đến đâu cũng khó thay đổi quan điểm, bởi lẽ những chân lý này khó thuyết phục chỉ qua chữ viết.

Tôi cũng cảm thấy hối lỗi khi trong 3 năm từ 2002-2004, khi còn in danh thiếp đề ca ngợi C++, đã đưa quá nhiều câu hỏi về OO vào đề thi tuyển dụng. Nhưng hiểu nhiều hơn bao giờ cũng tốt - mỗi khi đối mặt thiết kế mới, tôi lại có thêm lý do để từ chối OO/C++. Dù có dùng OO, tôi vẫn đảm bảo thiết kế đối tượng rõ ràng, nhưng tôi càng rõ rằng thực ra tôi hoàn toàn có thể không cần nó.

Hệ quả tự nhiên của hai nguyên tắc trên

  • Không bao giờ copy-paste code
  • Không thực hiện các chức năng tương tự nhiều lần
  • Không thiết kế cho nhu cầu chưa tồn tại - hãy tin rằng thiết kế hiện tại đủ rõ ràng để dễ dàng sửa đổi sau này…

Thiết kế mới đây
Tôi xin chia sẻ một thiết kế gần đây, không có gì đặc biệt nhưng đòi hỏi nhiều công sức. Tổng dòng code không nhiều - khoảng 1000 dòng cho 3-4 ngày làm việc, thấp hơn

0%