Duy Nguyen Hoang A fully enthusiastic boy

10 Programming Principles Every Developer Should Know

19 min read

Bài viết được dịch từ: https://blog.ossph.org/programming-principles-every-developer-should-know/

Trong bài viết này chúng ta sẽ tìm hiểu về các nguyên tắc cơ bản mà các nhà phát triển cần tuân theo để cho ra đời những đoạn mã nguồn tốt, “sạch sẽ”, dễ đọc và dễ duy trì.

10 programming principles

1. DRY (Don’t’ Repeat Yourself)

Nguyên tắc lập trình đầu tiên trong danh sách này là DRY, viết tắt của “Don’t Repeat Yourself” (Đừng Lặp Lại Chính Mình). Điều này có nghĩa là bạn nên tránh sao chép mã trong chương trình của mình và thay vào đó cố gắng viết mã có thể tái sử dụng và tách rời.

Sao chép mã có thể gây ra nhiều vấn đề, như tăng cường công sức bảo trì, khả năng gây ra lỗi cao hơn và khó khăn trong việc thay đổi trên nhiều nơi.

Hãy tưởng tượng bạn đang xây dựng một chương trình tính diện tích các hình khác nhau. Thay vì viết các hàm riêng để tính diện tích của từng hình (ví dụ: một hàm riêng để tính diện tích của một hình vuông, một hàm khác cho tam giác, và cetera), bạn có thể tuân theo nguyên tắc DRY bằng cách tạo ra một hàm duy nhất được gọi là calculateArea (tính diện tích) nhận các tham số cần thiết và trả về diện tích.

Như vậy, bạn có thể tái sử dụng cùng một hàm cho tất cả các hình bằng cách truyền vào các tham số thích hợp. Bạn tránh việc phải lặp lại cùng một logic cho việc tính diện tích, giúp mã của bạn trở nên hiệu quả và dễ bảo trì hơn.

Hãy nhớ rằng tuân thủ nguyên tắc DRY không chỉ giúp bạn viết mã sạch sẽ và có tổ chức hơn mà còn tiết kiệm thời gian và công sức trong tương lai.

2. KISS (Keep it Simple, Stupid)

KISS (Keep It Simple, Stupid) nhấn mạnh tầm quan trọng của sự đơn giản trong phát triển phần mềm. Nó cho rằng chúng ta nên cố gắng giữ mã và giải pháp đơn giản nhất có thể. Đơn giản hóa mã làm cho nó dễ hiểu, dễ bảo trì và dễ gỡ lỗi, giảm khả năng gặp lỗi hoặc vấn đề.

Ví dụ, giả sử chúng ta cần viết một chương trình để tính trung bình của một danh sách số. Một cách tiếp cận đơn giản và trực tiếp là duyệt qua danh sách, tổng hợp các số, sau đó chia tổng cho tổng số lượng. Cách tiếp cận này dễ hiểu và chỉ đòi hỏi một vài dòng mã.

Mặt khác, một cách tiếp cận phức tạp hơn có thể liên quan đến việc sử dụng các công thức toán học phức tạp hơn hoặc tích hợp các tính năng không cần thiết làm phức tạp logic. Sự phức tạp này có thể làm cho mã khó hiểu và khó bảo trì trong tương lai.

Đơn giản hóa mã giúp cải thiện khả năng hiểu và khả năng thích ứng của nó cho cả bạn và các nhà phát triển khác trong tương lai. Ngoài ra, nó giảm khả năng gây ra lỗi.

3. YAGNI (You Aren’t Gonna Need It)

YAGNI (You Aren’t Gonna Need It – Bạn Sẽ Không Cần Nó) là một nguyên tắc hữu ích cho nhà phát triển phần mềm. Nó nhắc nhở chúng ta tránh việc thêm các tính năng hoặc chức năng không cần thiết vào mã của chúng ta. Nói cách khác, đừng viết mã cho những thứ bạn không cần hiện tại hoặc không dự đoán sẽ cần trong tương lai. Nguyên tắc này khuyến khích tính đơn giản và hiệu quả trong phát triển phần mềm.

Để minh họa nguyên tắc YAGNI, hãy xem một tình huống trong đó chúng ta đang phát triển một chương trình để quản lý một danh sách công việc cần làm. Sử dụng YAGNI đồng nghĩa tập trung hoàn toàn vào việc triển khai các chức năng cần thiết để xử lý các công việc, chẳng hạn như thêm, xóa và đánh dấu chúng là đã hoàn thành. Nó cảnh báo không nên đưa vào các chức năng phức tạp như nhắc nhở, thông báo hoặc mã màu trừ khi chúng thực sự cần thiết cho chức năng cơ bản của chương trình.

Tuân thủ ý tưởng YAGNI cho phép chúng ta tiết kiệm thời gian và công sức bằng cách tránh xây dựng các tính năng không cần thiết có thể chưa bao giờ được sử dụng hoặc có thể được thêm sau nếu cần. Nguyên tắc này hữu ích trong việc duy trì một nguồn mã sạch và quản lý được, giảm nguy cơ phát hiện lỗi.

4. Separation of Concerns – SoC

Chia thành các quyền quan tâm (Separation of Concerns – SoC) là nguyên tắc cốt lõi trong phát triển phần mềm, khuyến khích phân tách một chương trình thành các phần riêng biệt và độc lập, trong đó mỗi phần giải quyết một quan tâm hay trách nhiệm cụ thể.

Nói một cách đơn giản, điều này có nghĩa là các phần khác nhau của chương trình nên tập trung vào việc làm một việc một cách tốt, mà không rối ren với các nhiệm vụ không liên quan. Tiếp cận này giúp cải thiện khả năng bảo trì mã, tính mô-đun và khả năng tái sử dụng.

Ví dụ, giả sử bạn đang xây dựng một ứng dụng web cho phép người dùng đăng ký và đăng nhập. Áp dụng nguyên tắc SoC, bạn sẽ tách chức năng đăng ký người dùng khỏi chức năng đăng nhập. Điều này có nghĩa là tạo ra các module hoặc hàm riêng biệt để xử lý mỗi vấn đề một cách độc lập. Điều này đảm bảo rằng mã phụ trách việc đăng ký người dùng tập trung chỉ vào công việc đó, trong khi mã phụ trách đăng nhập xử lý xác thực và phân quyền.

Việc phân tách này giúp dễ dàng cập nhật hoặc thay đổi một phần của ứng dụng mà không ảnh hưởng đến các phần khác. Ngoài ra, nó cho phép các thành viên khác nhau trong nhóm làm việc trên các phần quan tâm khác nhau đồng thời, cải thiện sự cộng tác và hiệu suất phát triển.

5. Do The Simplest Thing That Could Possibly Work

Hãy làm điều đơn giản nhất có thể (Do The Simplest Thing That Could Possibly Work) nhấn mạnh tầm quan trọng của sự đơn giản trong phát triển phần mềm. Thay vì làm phức tạp các giải pháp, nhà phát triển nên tìm cách tiếp cận đơn giản và tối thiểu nhất để đáp ứng các yêu cầu ngay lập tức. Nguyên tắc này khuyến khích tránh sự phức tạp không cần thiết, điều này có thể dẫn đến mã quản lý và bảo trì dễ dàng hơn.

Ví dụ, giả sử bạn được giao nhiệm vụ phát triển một chương trình tính trung bình của một danh sách số. Thay vì thiết kế một thuật toán phức tạp với nhiều bước và các công thức toán học tiên tiến, bạn có thể tuân theo nguyên tắc đơn giản. Bạn có thể bắt đầu bằng cách cộng tất cả các số trong danh sách và sau đó chia tổng cho tổng số lượng số.

Cách tiếp cận đơn giản này đạt được kết quả mong muốn mà không có sự phức tạp hoặc tính toán không cần thiết. Tập trung vào giải pháp đơn giản không chỉ tiết kiệm thời gian và công sức, mà còn tạo ra mã dễ hiểu, dễ gỡ lỗi và dễ bảo trì trong tương lai.

6. Code For The Maintainer

Viết mã cho người bảo trì (Code For The Maintainer) có nghĩa là viết mã một cách dễ dàng cho những nhà phát triển khác hiểu, sửa đổi và bảo trì sau này. Như một nhà phát triển phần mềm, việc xem xét những người sẽ làm việc với mã của bạn sau khi bạn hoàn thành là rất quan trọng. Giống như một cuốn sách tốt được viết với người đọc trong tâm trí, mã tốt cũng nên được viết với người bảo trì trong tâm trí.

Một cách để đạt được tính bảo trì của mã là tuân theo các quy ước và thực tiễn viết mã đã được thiết lập. Ví dụ, sử dụng tên biến và tên hàm mô tả giúp cải thiện tính đọc hiểu. Thay vì sử dụng các tên mập mờ như a, b hoặc x, hãy sử dụng các tên có ý nghĩa mô tả rõ mục đích và chức năng của mã.

Ngoài ra, sắp xếp mã thành các phần hợp lý, thêm chú thích để giải thích các phần phức tạp hoặc khó hiểu, và phân tách các nhiệm vụ phức tạp thành các hàm nhỏ, dễ quản lý cũng làm cho mã dễ hiểu và dễ bảo trì.

Áp dụng các kỹ thuật này có thể giúp những nhà phát triển trong tương lai cần làm việc với mã của bạn hiểu nó tốt hơn, giảm khả năng gây ra lỗi hoặc hành vi không mong muốn trong quá trình bảo trì và nâng cấp. Cuối cùng, viết mã cho người bảo trì đảm bảo rằng phần mềm ổn định và có thể tiến hóa một cách liền mạch trong thời gian.

7. Avoid Premature Optimization

Hạn chế tối ưu hóa sớm (Avoid Premature Optimization) nhắc nhở nhà phát triển phần mềm ưu tiên viết mã sạch và chức năng trước khi tập trung vào tối ưu hóa hiệu suất. Tối ưu hóa sớm đề cập đến việc dành quá nhiều thời gian và công sức vào việc tối ưu hóa mã có thể không cần thiết. Thay vào đó, nhà phát triển nên tập trung vào việc tạo ra mã dễ hiểu và bảo trì và đáp ứng các yêu cầu chức năng mong muốn.

Hãy tưởng tượng bạn đang xây dựng một chương trình để tính tổng của tất cả các số trong một danh sách cho trước. Như một nhà phát triển, bạn có thể bị kích thích để dành nhiều thời gian tối ưu hóa mã để chạy nhanh nhất có thể. Tuy nhiên, nếu bạn ưu tiên tối ưu hóa sớm, bạn có thể kết thúc với mã phức tạp và khó hiểu, dễ gây lỗi. Thay vào đó, theo nguyên tắc tránh tối ưu hóa sớm, bạn sẽ tập trung vào việc viết một giải pháp đơn giản và trực tiếp hoạt động đúng.

Sau khi mã hoạt động và đáp ứng yêu cầu, bạn có thể phân tích hiệu suất và tối ưu hóa nếu cần, dựa trên các mô hình sử dụng thực tế hoặc đo lường hiệu suất. Điều này đảm bảo rằng thời gian và công sức của bạn được sử dụng một cách hợp lý và tránh sự phức tạp quá mức trong các giai đoạn đầu của quá trình phát triển.

8. Boy Scout Rule

Nguyên tắc của Hướng đội trưởng (Boy Scout Rule) khuyến khích nhà phát triển phần mềm để lại nguồn mã trong trạng thái tốt hơn so với khi tìm thấy nó. Nó khuyến khích ý tưởng liên tục cải thiện chất lượng mã thông qua các thay đổi nhỏ, tuần tự mỗi khi bạn làm việc với nó. Giống như đội trưởng của Hội Hướng đạo để lại một khu trại sạch sẽ hơn so với khi họ tìm thấy nó, nhà phát triển nên cố gắng để mã được sắp xếp, dễ đọc và dễ bảo trì hơn sau khi thực hiện các thay đổi.

Ví dụ, giả sử bạn đang làm việc trên một dự án phần mềm và bạn gặp một đoạn mã khó hiểu hoặc có thể viết một cách hiệu quả hơn. Thay vì chỉ thực hiện những thay đổi cần thiết và tiếp tục, nguyên tắc Hướng đội trưởng đề xuất bạn dành một chút thời gian bổ sung để cải thiện mã. Điều này có thể bao gồm đổi tên biến để mô tả rõ ràng hơn, đơn giản hóa logic phức tạp hoặc tái cấu trúc mã để tuân theo các quy tắc tốt nhất.

Áp dụng nguyên tắc Hướng đội trưởng không chỉ giải quyết vấn đề hiện tại mà còn cải thiện nguồn mã cho những nhà phát triển trong tương lai.

9. Law of Demeter

Luật Demeter (Law of Demeter) là một hướng dẫn giúp nhà phát triển viết mã có tính mô-đun cao và không phụ thuộc quá nhiều vào chi tiết bên trong của các thành phần khác. Ý tưởng chính sau nguyên tắc này là tối thiểu hóa sự kết nối giữa các phần khác nhau của hệ thống phần mềm.

Nói một cách đơn giản, nó cho rằng một module chỉ nên có kiến thức hạn chế về cấu trúc nội bộ của các module khác và chỉ tương tác với những láng giềng trực tiếp.

Hãy tưởng tượng một tình huống trong đó chúng ta có một đối tượng gọi là Person (Người) có các thuộc tính và hành vi khác nhau. Theo Luật Demeter, nếu chúng ta muốn truy cập vào một thuộc tính địa chỉ của người, thay vì truy cập trực tiếp như person.address.street, chúng ta nên sử dụng một phương thức được cung cấp bởi đối tượng person chính nó, chẳng hạn như person.getStreet(). Điều này cho phép đối tượng Person đóng gói các chi tiết của địa chỉ của nó và tiếp xúc với một giao diện cấp cao hơn cho các thành phần khác tương tác với nó.

Tuân theo Luật Demeter dẫn đến mã linh hoạt hơn và dễ bảo trì hơn. Nếu cấu trúc nội bộ của đối tượng Person hoặc địa chỉ của nó thay đổi, chúng ta chỉ cần cập nhật các phương thức trong đối tượng Person, thay vì sửa đổi tất cả các vị trí trong mã mà địa chỉ được truy cập trực tiếp. Nguyên tắc này khuyến khích kết nối lỏng lẻo, giảm sự phụ thuộc và cải thiện mô-đun toàn bộ hệ thống phần mềm của chúng ta.

10. SOLID

Các nguyên tắc SOLID là một tập hợp năm nguyên tắc thiết kế giúp các nhà phát triển phần mềm tạo ra mã nguồn dễ bảo trì và linh hoạt. Những nguyên tắc này cung cấp hướng dẫn để viết mã nguồn sạch, có tính mô-đun và mở rộng. Hãy cùng tìm hiểu từng nguyên tắc và hiểu chúng thông qua các ví dụ.

Single Responsibility Principle (SRP)

Nguyên tắc này khẳng định rằng một lớp hoặc module chỉ nên có một lý do để thay đổi, nghĩa là nó chỉ nên có một trách nhiệm duy nhất. Các lớp tập trung vào một mục đích duy nhất sẽ dễ hiểu, kiểm thử và thích ứng hơn. Ví dụ, hãy xem một lớp được gọi là EmailSender. Nó nên chỉ chịu trách nhiệm gửi email mà không xử lý bất kỳ công việc không liên quan nào khác như tạo báo cáo hoặc phân tích dữ liệu. Bằng cách tuân thủ SRP, chúng ta duy trì mã nguồn dễ bảo trì và có tính mô-đun cao hơn.

Open/Closed Principle (OCP)

Nguyên tắc OCP nhấn mạnh rằng các thực thể phần mềm (lớp, module, hàm) nên được mở rộng nhưng đóng lại cho việc sửa đổi. Điều này có nghĩa là chúng ta nên có thể thêm tính năng hoặc hành vi mới mà không cần sửa đổi mã nguồn hiện tại. Một cách để đạt được điều này là sử dụng kế thừa hoặc giao diện. Ví dụ, hãy tưởng tượng một lớp hình học có các lớp con khác nhau như Rectangle và Circle. Nếu chúng ta muốn thêm một hình học mới, chúng ta có thể tạo một lớp con mới mà không cần sửa đổi lớp Shape hiện tại. Nguyên tắc này khuyến khích tính tái sử dụng mã nguồn và giảm nguy cơ gây ra lỗi trong mã nguồn đã hoạt động.

Liskov Substitution Principle (LSP)

Nguyên tắc LSP khẳng định rằng các đối tượng của một lớp cha có thể thay thế bằng các đối tượng của các lớp con của nó mà không ảnh hưởng đến tính đúng đắn của chương trình. Đơn giản mà nói, bất kỳ thể hiện nào của một lớp cũng nên có thể được sử dụng thay cho lớp cha của nó mà không gây ra hành vi không mong muốn. Ví dụ, hãy nghĩ về một lớp cơ sở gọi là Animal với một phương thức makeSound(). Các lớp con như Cat và Dog nên có thể thay thế lớp Animal mà vẫn có thể tạo ra hành vi mong đợi mà không gây ra lỗi hoặc không nhất quán.

Interface Segregation Principle (ISP)

Nguyên tắc ISP khuyến nghị rằng các client không nên bị ép buộc phụ thuộc vào các giao diện mà họ không sử dụng. Nó khuyến khích việc tạo ra các giao diện cụ thể phù hợp với nhu cầu của client thay vì có các giao diện lớn, khối monolithic. Điều này giúp các lớp không cần phải triển khai các phương thức không liên quan đến chúng. Ví dụ, hãy tưởng tượng một giao diện gọi là Printer với các phương thức như print(), scan(), và fax(). Thay vì có một giao diện duy nhất, tốt hơn là chia thành các giao diện nhỏ hơn như Printable, Scannable, và Faxable. Như vậy, các lớp có thể triển khai chỉ các giao diện mà họ cần, giữ mã nguồn sạch hơn và tập trung hơn.

Dependency Inversion Principle (DIP)

Nguyên tắc DIP cho biết các module cấp cao không nên phụ thuộc vào module cấp thấp; cả hai đều nên phụ thuộc vào các trừu tượng. Nó khuyến khích việc lỏng kết nối và cho phép dễ dàng thay đổi và kiểm thử. Thực tế, điều này có nghĩa là các lớp nên phụ thuộc vào các giao diện hoặc lớp trừu tượng thay vì các cài đặt cụ thể. Ví dụ, xem xét một lớp gọi là Logger cần ghi nhật ký vào một tệp. Thay vì phụ thuộc trực tiếp vào một cài đặt cụ thể của hệ thống tệp, nó nên phụ thuộc vào một giao diện như FileSystem, có thể có nhiều cài đặt khác nhau (ví dụ: LocalFileSystem, CloudFileSystem). Như vậy, chúng ta có thể chuyển đổi giữa các cài đặt mà không cần sửa đổi lớp Logger.

Các nhà phát triển phần mềm có thể tạo mã nguồn dễ bảo trì, mở rộng và linh hoạt hơn bằng cách tuân thủ các nguyên tắc SOLID. Những nguyên tắc này khuyến khích tính mô-đun, tính tái sử dụng và kiểm thử dễ dàng, điều này cuối cùng dẫn đến phần mềm chất lượng cao hơn. Mặc dù chúng có thể đòi hỏi một số công sức và kế hoạch thêm ban đầu, nhưng lợi ích lâu dài làm cho chúng trở thành những hướng dẫn quý giá để tuân thủ trong quá trình phát triển phần mềm.

Kết luận

Hiểu và áp dụng các nguyên tắc lập trình là rất quan trọng đối với mỗi nhà phát triển phần mềm. Những nguyên tắc này cung cấp một tập hợp các hướng dẫn và phương pháp tốt nhất giúp tạo ra mã nguồn sạch, hiệu quả và dễ bảo trì. Nhà phát triển có thể cải thiện tính tái sử dụng, tính mô-đun và tính linh hoạt của mã nguồn bằng cách tuân thủ những nguyên tắc này, giúp tạo ra các giải pháp phần mềm có khả năng mở rộng và mạnh mẽ hơn. Ngoài ra, những nguyên tắc này khuyến khích các thói quen lập trình tốt, cải thiện sự cộng tác giữa các thành viên trong nhóm và cuối cùng đóng góp vào thành công của dự án phần mềm. Khi phát triển phần mềm tiếp tục tiến xa, việc ứng dụng những nguyên tắc lập trình này sẽ giúp nhà phát triển viết mã nguồn chất lượng cao đáp ứng được yêu cầu của cảnh quan công nghệ thay đổi liên tục ngày nay.

Avatar photo
Duy Nguyen Hoang A fully enthusiastic boy

One Reply to “10 Programming Principles Every Developer Should Know”

Leave a Reply

Your email address will not be published. Required fields are marked *