Mã sạch: Tên có ý nghĩa (Phần 1)

Tên xuất hiện ở khắp nơi trong phần mềm. Chúng ta đặt tên cho biến, hàm, danh sách tham số, lớp, gói. Sau đó chúng ta đặt tên tệp và tên thư mục chứa chúng. Rồi chúng ta đặt tên tệp jar và tệp war, tệp ear. Chúng ta đặt tên, đặt tên và đặt tên. Vì chúng ta phải làm điều đó rất nhiều, nên chúng ta cần phải làm tốt. Sau đây là một số quy tắc đơn giản để tạo ra một cái tên tốt.

Sử dụng những tên gợi tả mục đích

Thật đơn giản để nói về những cái tên được gợi tả. Điều mà chúng tôi muốn bạn ghi nhớ đó là chúng ta cần nghiêm túc về vấn đề này. Việc lựa chọn tên tốt mất nhiều thời gian nhưng lại tiết kiệm được rất nhiều khi dùng. Vì vậy cần chú ý tới những cái tên bạn đặt và thay đổi chúng khi bạn tìm thấy tên tốt hơn. Khi bạn làm được điều đó, tất cả những ai khi đọc đoạn mã của bạn (bao gồm cả bạn) sẽ thấy dễ hiểu hơn.

Tên của biến, hàm, lớp nên giải đáp cho tất cả những câu hỏi lớn. Nó sẽ cho bạn biết lý do tại sao nó tồn tại, những gì nó làm, và cách nó được sử dụng. Nếu một cái tên cần được yêu cầu giải thích, thì tên này không có ý nghĩa gợi tả.

int d; // thời gian trôi qua trong ngày

Tên biến d cho thấy không có nghĩa gì. Nó không diễn tả cảm giác thời gian trôi qua, cũng không phải nói về ngày. Chúng ta nên chọn một tên mô tả được điều đang diễn ra như:

ma1

Sự chọn lựa những tên gợi tả làm cho ta dễ dàng hiểu và thay đổi khi lập trình hơn. Mục đích của đoạn mã này là gì?

ma2

Tại sao lại khó có thể giải thích đoạn mã này đang làm gì? Không có biểu thức phức tạp, khoảng cách và thụt đầu dòng hoàn toàn hợp lý. Chỉ có ba biến và hai hằng được đề cập đến. Không hề có bất kỳ lớp hoặc phương thức đa hình, dường như chỉ là một danh sách mảng.

Vấn đề không phải là sự đơn giản của đoạn mã, nhưng có tính “không tường minh” của đoạn mã (để đồng xu cụm từ): mức độ mà bối cảnh là không rõ ràng trong các mã chính nó. Mã ngầm đòi hỏi chúng ta biết câu trả lời cho những câu hỏi như:

1. theList thuộc loại nào?

2. Nghĩa của chỉ số bắt đầu từ không của một phần tử trong theList là gì?

3. Số 4 có nghĩa gì?

4. Tôi sử dụng danh sách trả về như thế nào?

Câu trả lời cho những câu hỏi không thấy trong đoạn mã trên, nhưng chúng có thể đã có. Hãy hiểu rằng chúng ta đang làm việc với trò chơi dò mình. Chúng ta thấy bảng chứa danh sách các ô được gọi là theList. Hãy đặt lại tên thành gameBoard.

Mỗi ô trên bảng được biểu diễn bởi một mảng. Chúng ta cũng thấy rằng chỉ số thứ 0 là vị trí của một trạng thái và giá trị trạng thái 4 có nghĩa là “được đánh dấu” (flagged). Bằng cách đưa ra những tên khái niệm chúng ta có thể cải tiến đáng kể đoạn mã:

ma3

Chú ý rằng tính đơn giản của đoạn mã trên là không thay đổi. Đoạn mã vẫn có chính xác các hằng và toán tử cùng mức lồng nhau. Nhưng đoạn mã trên đã trở nên tường minh hơn.

Chúng ta có thể phát triển thêm, thay vì sử dụng mảng số nguyên, ta tạo ra một lớp đơn giản cho các ô. Lớp này chứa một hàm gợi tả (gọi là isFlagged) để ẩn đi các con số ma thuật. Đây là phiên bản mới của hàm:

ma4

Với những thay đổi tên đơi giản, thật không khó khăn để có thể hiểu điều gì đang diễn ra. Đây chính là điểm mạnh của việc chọn được những cái tên tốt.

Tránh sai lạc

Lập trình viên phải tránh để lại manh mối sai mà che khuất nghĩa đoạn mã lệnh. Chúng ta nên tránh những từ có nghĩa khác với ý định thực của mình. Ví dụ, hp, aix, sco là những tên biến kém bởi chúng là những cái tên mô tả trong hệ điều hành Unix hay các phiên bản của nó. Thậm chí nếu bạn đang lập trình về hypotenuse và hp nhìn giống như từ viết tắt, đây không phải là thông tin tốt.

Không nên gọi một nhóm các tài khoản (account) là accountList trừ khi nó lưu trữ danh sách (list) thật sự. Từ danh sách (list) có nghĩa là một cái gì đó cụ thể cho các lập trình viên. Nếu các tài khoản này không được lưu trữ ở dạng danh sách, thì có thể danh đến những kết luận sai lệch. Vì vậy cái tên accoutGroup hoặc bunchOfAccounts chỉ ra danh sách chủ tài khoản vẫn tốt hơn.

Hãy cẩn trọng khi sử dụng những tên không khác nhau nhiều. Bao lâu để phát hiện sự khác biệt giữa XYZControllerForEfficientHandlingOfStrings trong một module và XYZControllerForEfficientStorageOfStrings ở một nơi khác không xa lắm. Chúng có hình dạng giống nhau kinh khủng.

Các biến có cách viết tương tự nhau cũng có vấn đề như khi tương tự nhau về thông tin. Sử dụng cách viết không nhất quán cũng dẫn đến việc hiểu sai lạc. Với môi trường lập trình Java hiện đại (IDE), chúng ta có sử dụng tính năng tự động hoàn thành mã. Chúng ta viết một vài ký tự của tên và nhấn phím tắt (nếu có) và IDE cho bạn một danh sách các tên để hoàn tất cho việc đặt tên. Điều này thật hữu ích nếu những cái tên có nghĩa tương tự nhau, được sắp xếp theo thứ tự bảng chữ cái, bởi vì các nhà phát triển đôi khi muốn chọn một cái tên mà không xem ý kiến của ai thậm chí các phương thức của lớp đó.

Một ví dụ thực sự khủng khiếp của tên gây hiểu sai khi sử dụng ký tự thường của L hoặc ký tự hoa của o làm tên biến. Tất nhiên vấn đề ở chỗ là chúng ta nhìn thấy các biến này gần giống con số 1 hoặc 0.

ma5

Người đọc có thể nghĩ đây là một cái bẫy, và chúng ta phải kiểm tra những đoạn mã như vậy. Trong trường hợp tác giả của đoạn mã đề nghị sử dụng một phông chứ khác để phân biệt sự khác nhau, một giải pháp sẽ khuyên họ cần viết tài liệu rõ ràng hoặc trao đổi để hiểu vấn đề rõ ràng hơn. Nhưng vấn đề ở chỗ nên dứt khoát khuyên họ sửa lại thành tên rõ ràng hơn.

Tạo nên sự khác biệt rõ ràng

Lập trình viên sẽ tự tạo ra vấn đề khi họ viết mã chỉ để thỏa mãn trình biên dịch hoặc trình thông dịch. Ví dụ, bởi vì bạn không thể sử dụng cùng một tên để chỉ hai việc khác nhau trong cùng một phạm vi, bạn có thể bị cám dỗ để thay đổi tên một tên theo cách ngẫu nhiên. Đôi khi, điều này được thực hiện do lỗi chính tả, dẫn đến tình huống ngạc nhiên sửa lỗi chính tả dẫn đến đoạn mã không có khả năng biên dịch được (Ví dụ khi đặt tên lớp là kclass bởi tên class đã được sử dụng cho việc khác).

Là không đủ khi thêm một dãy số hay từ nóng, thậm chí trình biên dịch vẫn thỏa mãn. Nếu có ý nghĩa khác nhau, thì chúng phải có tên khác nhau.

Đặt tên cho một dãy số là (a1, a2, …, aN) là ngược lại với việc đặt tên có chủ đích. Những tên này không làm hiểu sai, nhưng chúng cũng không có thông tin. Chúng không cung cấp manh mối về ý tưởng của tác giả. Hãy xem xét:

ma6

Hàm này sẽ dễ hiểu hơn khi biến source và destination được sử dụng như tên tham số truyền vào.

Những từ nhiễu là một trường hợp khác của tên mà không có sự khác biệt đáng kể. Hãy tưởng tượng rằng bạn có lớp Product. Nếu bạn có một lớp khác được gọi ProductInfo hay ProductData. Bạn đặt những tên khác nhau mà không tạo ra bất kỳ sự khác biệt nào. Info và Data là hai từ nhiễu.

Lưu ý rằng quy ước sử dụng tiền tố (như a, the) không có vấn đề gì sai bởi chúng tạo nghĩa khác biệt. Ví dụ bạn phải sử dụng a cho tất biến cục bộ và the cho tất cả các tham số của hàm. Những sẽ là vấn đề khi bạn quyết định gọi tên biến theZork bởi vì bạn đã có một tên biến khác được đặt là zork.

Các từ nhiễu là thừa. Một biến không bao giờ có tên variable. Từ table không nên xuất hiện trong tên bảng. Tên NameString tốt hơn Name ở chỗ nào? Name đã bao giờ là số thực chưa? Nếu như vậy nó phát vỡ quy tắc trước về sự sai lệnh thông tin. Hãy tưởng tượng việc tìm ra tên lớp là Customer và một cái tên khác đặt là CustomerObject. Bạn thấy sự khác biệt giữa hai cái tên này là gì? Tên nào sẽ mô tả tốt nhất cho lịch sử thanh toán của một khách hàng?

Có một ứng dụng mà chúng ta biết để minh họa điều này. Chúng tôi đã đổi tên để bảo vệ lỗi, nhưng đây là sự chính xác của lỗi:

ma7

Làm thế nào để lập trình viên trong dự án biết các hàm được gọi?

Trong trường hợp không có quy ước cụ thể, biến moneyAmount là không có sự khác biệt với tiền, customerInfo không có sự khác biệt với customer, accountData không có sự khác biệt với account, và theMessage không có sự khác biệt với message. Các tên khác biệt trong trường hợp này giúp người được biết sự khác biệt được đưa ra.

Sử dụng tên đọc được

Hummans là từ có nghĩa tốt. Một phần quan trọng của bộ não chúng ta là dành riêng các các khái niệm về từ. Những từ này được định nghĩa hay phát âm. Nó sẽ là một sự sai lầm khi không thể tận dụng những ưu điểm lớn này của bộ não chúng ta để đối phó với ngôn ngữ nói. Vì vậy tên của bạn được phát âm.

Nếu bạn không thể phát âm những cái tên, bạn không thể thảo luận về nó mà không suy nghĩ gì không khác nào một tên ngốc.

Một công ty mà tôi biết là genymdhms (ngày thành lập, tháng, năm, giờ, phút, giây) họ đi xung quanh và nói “gen why emm dee aich emm es”. Tôi có thói quen không tốt khi phát âm các vấn đề như văn bản, do vậy tôi bắt đầu nói “gen-yah-mudda-hims”.

Nó sau đó đã được gọi bởi một loạt các nhà thiết kế và các nhà phân tích, và chúng tôi vẫn có vẻ ngớ ngẩn. Nhưng chúng tôi đã ở trên các trò đùa, vì vậy nó rất thú vị. Vui vẻ hay không, chúng tôi đã dung túng đặt tên nghèo. Phát triển mới phải có các biến giải thích cho họ, và sau đó họ nói về nó trong ngớ ngẩn từ hư cấu thay vì sử dụng thuật ngữ tiếng Anh phù hợp. So sánh

ma8

Với

ma9

Cuộc trò chuyện hay hơn bây giờ có thể: “Này, Mikey, hãy xem hồ sơ này! Dấu thời gian thế hệ được thiết lập để ngày ngày mai! Làm thế nào mà có thể được? ”

Sử dụng tên tìm kiếm được

Tên đơn thư và hằng số có một vấn đề đặc biệt ở chỗ chúng không dễ dàng để xác định vị trí trên một cơ thể của văn bản.

Nếu biến và hằng được sử dụng nhiều nới trong đoạn mã. Cần tạo ra tên tìm kiếm thân thiện.

So sánh:

ma10

Với

ma11

Chú ý biến sum, không phải là một cái tên đầy đủ hữu ích vì ít nhất cũng có thể tìm được. Sự cố ý đặt đoạn mã nhưng lại dễ dàng tìm WORK_DAYS_PER_WEEK hơn là đặt với con số 5 và đọc tiếp xuống chỉ còn các trường hợp với nghĩa như mong muốn.

Tránh trùng bảng mã

Chúng tôi có đủ mã hóa để đối phó với không bổ sung thêm vào gánh nặng của chúng tôi. Mã hóa loại, phạm vi thông tin vào tên đơn giản là thêm một gánh nặng thêm giải mã. Nó hầu như không có vẻ hợp lý để yêu cầu mỗi nhân viên mới để tìm hiểu thêm một mã hóa “ngôn ngữ” ngoài học tập (thường là đáng kể) cơ thể của mã mà họ sẽ làm việc vào Đây là một gánh nặng tinh thần không cần thiết khi cố gắng giải quyết một vấn đề. Tên mã hóa hiếm khi phát âm được và rất dễ sai loại.

Hungarian Notation

Trước đây, khi chúng ta làm việc với ngôn ngữ name-length-challenged, chúng ta đã vi phạm nguyên tắc cần thiết và đã phải hối tiếc. Fortran buộc phải mã hóa bằng cách làm cho chữ cái đầu tiên một mã số cho các loại. Phiên bản đầu của BASIC cho phép chỉ có một ký tự với một chữ số. Hungarian Notation (HN) đã diễn này lên một tầm cao mới.

HN được coi là trở lại khá quan trọng trong Windows C API, khi tất cả mọi thứ đều xử lý với số nguyên hoặc một con trỏ kiểu long hay một con trỏ kiểu void, hoặc một vài sự thực thi của “chuỗi” (với mục đích sử dụng và các thuộc tính khác nhau). Trình biên dịch đã không kiểm tra các kiểu trong những ngày đó, vì vậy các lập trình viên cần một cái nạng (crutch) để giúp họ nhớ các loại.

Trong ngôn ngữ lập trình hiện đại, các kiểu dữ liệu phong phú hơn, các trình biên dịch nhớ và thực thi được nhiều loại hơn. Hơn nữa, có xu hướng xây dựng các lớp nhỏ hơn và các hàm ngắn gọi hơn vì vậy người ta thường thấy điểm khai báo của mỗi biến mà họ đang sử dụng.

Lập trình viên Java không đến kiểu dữ liệu mã hóa. Các đối tượng là các kiểu dữ liệu mạnh và việc tạo nên môi trường tiến bộ như vậy giúp ta có thể xác định một loạt các lỗi trước khi trình biên dịch chạy. Vì vậy, hiện nay HN và các dạng thức khác nhau của các kiểu mã hóa chỉ là những trở ngại đơn giản. Chúng chỉ làm khó khăn hơn cho việc thay đổi tên hoặc kiểu dữ liệu của biến, hàm, lớp. Và cũng khó khăn cho việc đọc mã. Ngoài ra còn có thể tạo ra một hệ thống mã hóa sẽ gây hiểu lầm cho người đọc.

PhoneNumber phoneString;

// name not changed when type changed!

Thành phần tiền tố

Bạn không cần phải xác định tên với với tiền tố m_. Các lớp và hàm nên đủ nhỏ để không cần phải dùng đến các tiền tố đó. Và bạn nên sử dụng editing environment để đánh dấu hoặc bôi màu cho các lớp hay hàm để tạo nên sự khác biệt giữa chúng.

ma12

Bên cạnh đó, người ta thường bỏ tiền tố (hoặc hậu tố) để thấy được phần tên có ý nghĩa hơn. Chúng ta đọc đoạn mã ít tiền tố hơn là phải thấy các đoạn mã nhiều tiền tố. Điều cuối cùng là tiền tố trở nên lộn xộn đó là dấu hiệu của đoạn mã chưa có sự cải tiến.

Giao diện và cài đặt

Thỉnh thoảng có một vài trường hợp đặc biệt khi mã hóa. Ví dụ, nếu bạn đang xây dựng một Abstract Factory để tạo ra đối tượng Shapes (hình). Factory này sẽ được tạo dưới dạng là interface và sẽ được thực thi bởi một lớp cụ thể. Vậy bạn nên đặt tên thế nào cho Factory đó? IShapeFactory hay ShapeFactory? Tôi thích interface ShapeFactory hơn. Khi xác định I phía trước interface, có nhiều phân tán và thông tin không tốt. Vì tôi không muốn khách hàng của tôi biết rằng tôi đang bàn giao cho họ một interface. Tôi chỉ muốn họ biết rằng đó là một ShapeFactory. Do vậy để mã hóa hay thực thi interface tôi chọn sự thực thi (implementation). Gọi đó là ShapeFactoryImp, hoặc thậm chí CShapeFactory hơn là mã hóa interface.

Tên lớp

Lớp và đối tượng nên có danh từ hoặc cụm danh từ như Customer, WikiPage, Account, và AddressParser. Tránh dùng những từ như Manager, Processor, Data, Infor trong tên của một lớp. Tên một lớp không nên là một động từ.

Tên phương thức

Phương thức nên có động từ hoặc cụm động từ như postPayment, deletePage, hoặc save. Các phương thức truy xuất, thay đổi nên được đặt tên cho giá trị của chúng và tiền tốt get, get và is theo chuẩn của javabean.

string name = employee.getName();

customer.setName(“mike”);

if (paycheck.isPosted())…

Khi phương thức khởi tạo được nạp chồng, dùng phương thức tính với tên mô tả đối số truyền vào. Ví dụ:

Complex fulcrumPoint = Complex.FromRealNumber(23.0);

thường tốt hơn:

Complex fulcrumPoint = new Complex(23.0);

Hãy xem xét việc ép buộc dùng chúng bằng cách chuyển phương thức khởi tạo tương ứng thành private.

Nguồn: Tạp chí Lập trình

Chia sẻ bài viết ngay