Bất cứ ai cũng có thể viết code. Cái khó là làm sao code được cho tốt?
Chúng ta từng nghe các câu chuyện về code kiểu spaghetti kinh dị, các chuỗi code if-else loằng ngoằng, các chương trình mà chỉ cần thay đổi 1 biến thôi là toàn bộ chương trình có thể bị phá vỡ, các hàm làm người ta hoa cả mắt… Đó là điều thường xảy ra những người thiếu kinh nghiệm cứ cố hoàn thành một sản phẩm có thể chuyển giao.
Bạn đừng thỏa mãn với việc code của bạn có thể hoạt động mà hãy phấn đấu để code có thể hoạt động lâu dài – để sau này không chỉ bạn, mà bất cứ ai khác cũng có thể dùng. Để làm được điều đó, sau đây là một số nguyên tắc bạn phải nhớ như in.
- KISS
Nguyên tắc “Đơn giản thôi, đồ ngốc” (“keep it simple, stupid”) có thể áp dụng cho mọi mặt của cuộc sống, nhưng đặc biệt cần thiết cho các dự án lập trình vừa và lớn.
Nguyên tắc này bạn phải áp dụng ngay từ đầu khi bạn xác định phạm vi của của dự án. Bạn đam mê phát triển game không có nghĩa là bạn có thể tạo ra một thế hệ mới của Warcraft hay Grand Theft Auto đâu. Khi nghĩ rằng bạn đã đủ đơn giản hóa rồi, hãy đơn giản hóa nó thêm 1 lần nữa – bạn sẽ không tránh khỏi việc gặp những “tính năng kinh tởm” (feature creep), do dó, hãy bắt đầu từ những thứ nhỏ và đơn giản thôi.
Ngay cả khi đã đang code rồi, hãy code thật đơn giản. Code phức tạp cần nhiều thời gian hơn để thiết kế và viết, dễ bị lỗi, và khó sửa đổi sau này. Antoine de Saint-Exupery từng nói: “Hoàn hảo, không phải là khi không có gì để thêm, mà là khi không phải bớt thứ gì”.
- DRY
Để viết được code clean và dễ sửa đổi thì nguyên tắc “đừng bao giờ viết mã bị trùng lặp” là vô cùng quan trọng. Khi viết code, tất nhiên bạn muốn tránh trùng lặp dữ liệu và logic. Nếu bạn tự thấy mình đang viết lặp đi lặp lại cùng một đoạn code, bạn đang vi phạm nguyên tắc này rồi đấy.
Ngược lại với quy tắc DRY là WET: “viết mọi thứ hai lần” (“write everything twice”) (hoặc “lãng phí thời gian” – “waste everyone’s time”). Một trong những cách tốt nhất để kiểm tra WET là tự hỏi bản thân: nếu muốn thay đổi hành vi của chương trình, mình cần phải sửa đổi bao nhiêu vùng mã?
Giả sử bạn đang viết ứng dụng podcast. Trên trang tìm kiếm, bạn code để tìm nạp chi tiết của một podcast. Và trên trang podcast, bạn cũng code tìm nạp chi tiết podcast. Cùng một code như vậy trên trang yêu thích. Vậy cố gói gọn tất cả trong 1 hàm thôi để nếu sau này cần sửa đổi thì bạn chỉ cần thực hiện 1 lần.
- Mở / đóng
Cho dù bạn đang code trong Java hay các mô-đun Python, bạn nên ưu tiên các code cho phép mở rộng nhưng hạn chế sửa đổi. Nguyên tắc này áp dụng cho tất cả các loại project, nhưng đặc biệt quan trọng với các loại thư viện hoặc khung làm việc được hướng đến nhiều người sử dụng.
Ví dụ, bạn đang bảo trì lại khung làm việc GUI. Bạn mong muốn người dùng đích có thể trực tiếp sửa đổi mà vẫn tích hợp code của mình. Nhưng nếu sau đó tận 4 tháng bạn cho ra một bản cập nhật quan trọng thì sao? Thật khó để cập nhật chúng mà không ảnh hưởng những sửa đổi từ người dùng đích.
Thay vào đó, hãy hạn chế sửa đổi trực tiếp code mà khuyến khích mở rộng. Code ban đầu và các code sửa đổi sau sẽ được tách biệt. Lợi ích là gì? Code sẽ ổn định hơn (người dùng sẽ không vô ý phá vỡ các code căn bản) và khả năng bảo trì tốt hơn (người dùng chỉ phải lo về mã mở rộng). Nguyên tắc mở / đóng là chìa khóa để tạo ra một API tốt.
- Đối tượng -> Thừa kế
Nguyên lý “hợp đối tượng thay vì thừa kế lớp” cho rằng đối với đối tượng có hành vi phức tạp, nên bao gồm các thể hiện của nó cùng với các hành vi riêng lẻ hơn là dùng lại một lớp (class) và thêm các hành vi mới.
Lạm dụng thừa kế có thể dẫn đến hai vấn đề chính. Thứ nhất, sự phân cấp thừa kế có thể trở nên lộn xộn. Thứ hai, sẽ khó xác định hành vi của một trường hợp cụ thể hơn, đặc biệt khi bạn muốn áp dụng hành vi từ một nhánh thừa kế vào một nhánh khác.
Composition thì code sẽ sạch hơn, dễ bảo trì hơn và có độ linh hoạt gần như vô tận, miễn là bạn có thể xác định được hành vi bạn muốn. Mỗi hành vi là một class riêng, và bạn chỉ việc thêm các hành vi lẻ vào để tạo ra một hành vi phức tạp.
- Nguyên lý Single responsibility
Theo nguyên lý Single responsibility, mọi lớp hoặc mô-đun trong một chương trình chỉ nên cung cấp một chút chức năng cụ thể. Như Robert C. Martin đã nói, “Một lớp thì chỉ nên thay đổi vì một lí do duy nhất.”
Các lớp và mô-đun thường bắt đầu như thế, nhưng khi bạn thêm các tính năng và các hành vi mới, những class hay mô-đun này dễ cần đến hàng trăm hoặc thậm chí hàng ngàn dòng code. Đây chính là lúc bạn cần chia chúng ra thành nhiều lớp và mô-đun nhỏ hơn.
- Nguyên lý Separation of Concerns
Nguyên lý Separation of Concerns tương tự như nguyên lý Single responsibility nhưng ở một mức độ trừu tượng hơn. Về bản chất, một chương trình phải được thiết kế sao cho nó bao gồm nhiều gói không giống nhau, và những gói này không nên liên quan đến các gói khác.
Một ví dụ nổi tiếng về mô hình này là mô hình MVC (Model-View-Controller), MVC phân chia một chương trình thành ba vùng riêng biệt: dữ liệu (“mô hình”), logic (“controller”), và những gì mà người dùng đích nhìn thấy (“View”). Các biến thể của mô hình MVC rất hay gặp trong các khung làm việc của những trang web nổi tiếng nhất ngày nay.
Ví dụ: Mã xử lý việc tải và lưu dữ liệu vào cơ sở dữ liệu không cần phải biết cách hiển thị dữ liệu trên web. Mã rendering có thể lấy dữ liệu đầu vào từ người dùng đích, nhưng sau đó chuyển dữ liệu nhập vào mã logic để xử lý. Mỗi phần có đều hoạt động độc lập.
Điều này đã mang lại cho chúng ta mã mô đun, giúp bảo trì dễ dàng hơn nhiều. Và trong tương lai, nếu bạn cần code lại tất cả mã rendering, bạn hòan tòan không phải lo lắng xem dữ liệu được lưu hay mã logic được xử lý như thế nào.
- YAGNI
Nguyên tắc “bạn sẽ không cần nó” (you aren’t gonna need it) nghĩa là bạn không bao giờ nên code các chức năng mà sau này bạn mới cần. Việc đó rất tốn thời gian vì rất có thể bạn sẽ không cần – không chỉ vậy, mã của bạn còn trở nên phức tạp một cách không cần thiết.
Có thể xem đây là một ứng dụng cụ thể của nguyên lý KISS và phản bác lại những người quá tuân thêo định lý DRY. Các lập trình viên thiếu kinh nghiệm thường cố gắng viết mã trừu tượng và chung chung nhất có thể để tránh mã WET, nhưng quá nhiều mã như vậy lại làm cho mã của bạn không thể bảo trì được.
Bí quyết ở đây là chỉ áp dụng nguyên tắc DRY khi cần. Nếu bạn thấy các chuỗi code bị lặp lại, hãy trừu tượng hóa chúng – nhưng đừng bao giờ làm thế nếu chỉ một đoạn mã bị lặp lại. Không phải lúc nào cũng ‘thừa còn hơn thiếu’.
- Tránh optimize code sớm
Nguyên lý Avoid Premature Optimization cũng tương tự như nguyên lý YAGNI. Điểm khác biệt là YAGNI giải quyết việc thực hiện các hành vi chưa cần thiết trong khi nguyên tắc này xử lý việc tăng tốc các thuật toán chưa cần thiết.
Vấn đề là nếu optimize sớm thì bạn không bao giờ biết được những điểm mấu chốt của chương trình cho đến khi nó đi vào thực tế. Tất nhiên là người ta có thể đoán, thậm chí đôi khi đoán đúng. Nhưng thường thì bạn sẽ chỉ lãng phí thời gian tăng tốc độ một hàm không chậm như bạn tưởng, hoặc các hàm ít khi được dùng đến.
Hãy đi dần đến từng mục tiêu của bạn một cách đơn giản nhất có thể, sau đó profile mã của bạn để tìm ra những điểm mấu chốt thực sự.
- Refactor, Refactor, và Refactor
Một lập trình thiếu kinh nghiệm hiếm khi code được đúng ngay từ lần đầu. Khi bạn thực hiện những tính năng mới bóng bẩy thì nó mang lại cảm giác khá đúng, nhưng khi mã đó mở rộng, những tính năng mới sẽ bị ảnh hưởng bởi những code trước.
Codebase thì liên tục phát triển. Việc phải xem lại, viết lại, hoặc thậm chí thiết kế lại toàn bộ khối mã là hoàn toàn bình thường – và thậm chí là được khuyến khích. Vì so với ngày mới bắt đầu, giờ bạn đã hiểu hơn về nhu cầu của dự án, và bạn nên thường xuyên sử dụng kiến thức vừa mới học được để refactor mã cũ.
Cũng nên nhớ rằng refactor không nhất thiết phải là một công đoạn dài. Như câu nói truyền thống của the Boy Scouts of America: “Hãy làm khu cắm trại sạch hơn lúc các bạn mới đến đó”, vậy nếu cần kiểm tra hoặc sửa đổi mã cũ, hãy luôn làm cho chúng sạch hơn và trong trạng thái tốt hơn.
- Code sạch> Code thông minh
Nói đến mã sạch, hãy bỏ qua cái tôi của mình và quên việc viết mã thông minh đi. Mọi người biết tôi đang nói đến điều gì mà: Không ai thật sự quan tâm đến loại code nhìn như bài toán đố chứ không phải giải pháp, và được viết ra chỉ để khoe khoang sự thông minh.
Mã thông minh là mã chứa càng nhiều logic trong một dòng mã càng tốt. Nó còn là khai thác sự phức tạp của ngôn ngữ để viết các câu lệnh lạ nhưng có chức năng nhất định. Là điều khiến ai nói phải thốt lên khi xem code của bạn, “Khoan đã, gì thế này!”
Các lập trình viên giỏi đi kèm với code chạy tốt. Đưa ra bình luận khi cần. Tuân thủ hướng dẫn về kiểu code, dù là từ ngôn ngữ lập trình (như Python) hay một công ty (như Google). Hãy lưu ý đến từng ngôn ngữ và bỏ ngay kiểu code Java bằng Python hoặc ngược lại. Xem bài viết của chúng tôi về bí quyết để viết code sạch hơn.
Điều gì tạo nên một lập trình viên giỏi?
Nếu bạn hỏi năm người thì bạn sẽ nhận được 10 đáp án khác nhau đấy. Đối với tôi, một lập trình viên giỏi là người hiểu rõ code là để phục vụ người dùng đích, có kỹ năng làm việc nhóm và hoàn thành công việc của anh ta cẩn thận và đúng hạn.
Đừng quá lo lắng nếu bạn chỉ mới bắt đầu. Hãy học cách viết code thật thư giãn. Nếu bạn cảm thấy bế tắc, hãy đọc bài viết về cách vượt qua những rào cản của một lập trình viên của chúng tôi. Và nếu đơn giản bạn cảm thấy không yêu thích công việc viết code, hãy đọc bài Những dấu hiệu bạn sinh ra không phải để làm một lập trình viên.
Tuyết Nhung (dịch)
(Nguồn: http://www.makeuseof.com)
Leave a Reply