Tuần trước, tôi viết về method có giá trị trả về nhất quán, và cách chúng làm code đơn giản hơn. Nhưng điều đó xảy ra như thế nào? Làm sao refactoring đúng có thể làm code dễ làm việc hơn? Điều gì làm abstraction tốt thành tốt, abstraction xấu thành xấu? Tôi muốn nói, bạn không thể chỉ sắp xếp lại code ngẫu nhiên và kỳ vọng phần mềm chất lượng.
Nếu bạn có thể học tại sao các kỹ thuật này hoạt động, bạn sẽ hiểu rõ hơn nơi code cần giúp đỡ. Và bạn sẽ dùng đúng công cụ ở nơi quan trọng nhất.
Vấn đề khó khăn nhất trong phát triển phần mềm là gì?
"Chỉ có hai vấn đề khó trong Khoa học Máy tính: cache invalidation và đặt tên."
– Phil Karlton
Chắc chắn câu nói này khắp nơi. Nhưng thực sự có vấn đề khó hơn trong phát triển phần mềm thực tế: quản lý độ phức tạp. Độ phức tạp trong phát triển phần mềm có vài định nghĩa khác nhau. Ở cốt lõi, độ phức tạp của một chương trình là lượng thứ bạn phải giữ ở phía trước tâm trí khi đang làm việc trên nó.
Khi bạn viết nhiều phần mềm hơn, bạn sẽ giữ được nhiều thứ hơn trong đầu cùng lúc. Nhưng ngay cả dev tốt nhất cũng có giới hạn. Nếu project quá phức tạp, bạn sẽ quá tải những gì tâm trí xử lý được, và bắt đầu quên các khu vực khác của chương trình. Và bug tồi tệ nhất xuất hiện khi bạn thay đổi một phần của project mà không nhận ra nó ảnh hưởng như thế nào đến phần hoàn toàn khác của cùng project.
Đơn giản hóa thông qua giả định
Nếu bạn có trí nhớ siêu nhiên, và nhớ cách tất cả các phần khác nhau của chương trình khớp nhau, bạn sẽ ít bug hơn, đúng? Trí nhớ khó xây dựng. Nhưng bạn có thể có nhiều lợi ích tương tự từ việc có thể tập trung vào một thứ tại một thời điểm.
Nhiều best practice trong phát triển phần mềm là về việc giảm số thứ bạn phải giữ trong tâm trí cùng lúc. Một vài ví dụ:
-
Nhất quán trong giá trị trả về nghĩa là bạn chỉ phải nghĩ cách xử lý một loại data, thay vì loại data khác nhau trong tình huống khác nhau.
-
Abstraction là cách ẩn code sau một interface đơn giản hơn. Với abstraction tốt, bạn chỉ cần nghĩ abstraction nên hoạt động như thế nào, và không phải lo về nó hoạt động bên trong như thế nào.
-
Testing có thể ngăn bạn vô tình làm hỏng code ở khu vực này khi đang làm việc ở khu vực khác. Bạn có thể giả định bất cứ gì bạn vô tình làm hỏng sẽ bị bắt. Điều này có nghĩa là bạn có thể tập trung chỉ vào code bạn đang làm, và sửa những gì bạn làm hỏng sau.
-
Test-Driven Development sẽ giúp bạn viết code hoạt động như bạn mong đợi. Bạn có thể tập trung viết implementation đơn giản nhất của method mà pass test. Nếu nó quá đơn giản và không thực sự hoạt động như bạn mong đợi, test sẽ bắt nó.
Khi bạn dùng các kỹ thuật này đúng cách, bạn có thể làm một số giả định tốt. Với những giả định này, bạn không phải giữ ở phía trước tâm trí nhiều thứ nữa. Bạn có thể viết code tốt hơn, đáng tin cậy hơn, vì bạn không phải lo về hậu quả gần như vô hạn code có thể có trên hệ thống.
Mặt khác, dùng các kỹ thuật này sai chỗ sẽ ẩn code bạn thực sự cần thấy. Bằng cách ẩn code, giả định bạn làm đôi khi sẽ sai. Điều này làm hơn nữa có khả năng bạn sẽ làm hỏng nó!
Đó là lý do abstraction xấu tệ hơn không abstraction, và tại sao bạn không thể chỉ ném "extract method" vào code cho đến khi nó thần thánh trở nên tốt.
Refactoring đúng, nơi đúng
Code rõ ràng là code thẳng thắn về điều nó đang làm. Cho thấy những gì bạn cần biết quan trọng như ẩn những gì bạn không cần biết. Khi bạn thực hiện refactoring, hoặc dùng best practice phần mềm, bạn nên nghĩ về code bạn kết thúc với, và nó đã giúp giảm độ phức tạp như thế nào. Bạn có giúp phần code còn lại làm giả định tốt, đơn giản hóa? Hay bạn đang chôn kiến thức thiết yếu sau một interface quá đơn giản?