Tin tức mới

Mã sạch: Tên có ý nghĩa

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ền, 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.

Người dịch Phạm Anh Đới


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 điều được đó, 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ư:

[code language=”java”]
int elapsedTimeInDays;
int daysSinceCreation;
int daysSinceModification;
int fileAgeInDays;
[/code]

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ì?

[code language=”java”]
public List<int[]> getThem() {
List<int[]> list1 = new ArrayList<>();
for (int[] x : theList) {
if (x[0] == 4) {
list1.add(x);
}
}
return list1;
}
[/code]

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ị trang 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ã:

[code language=”java”]
public List<int[]> getFlaggedCells() {
List<int[]> flaggedCells = new ArrayList<int[]>();
for (int[] cell : gameBoard) {
if (cell[STATUS_VALUE] == FLAGGED) {
flaggedCells.add(cell);
}
}
return flaggedCells;
}
[/code]

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 nhàu. Nhưng đoạn mã trên đã trên 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:

[code language=”java”]
public List getFlaggedCells() {
List flaggedCells = new ArrayList();
for (Cell cell : gameBoard) {
if (cell.isFlagged()) {
flaggedCells.add(cell);
}
}
return flaggedCells;
}
[/code]

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 hà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.

[code language=”java”]
int a = l;
if (O == l) {
a = O1;
} else {
l = 01;
}
[/code]

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:

[code language=”java”]
public static void copyChars(char a1[], char a2[]) {
for (int i = 0; i < a1.length; i++) {
a2[i] = a1[i];
}
}
[/code]

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ờ nên 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:

[code language=”java”]
getActiveAccount();
getActiveAccounts();
getActiveAccountInfo();
[/code]

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 đượ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 này 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

[code language=”java”]
class DtaRcrd102 {

private Date genymdhms;
private Date modymdhms;
private final String pszqint = "102";
/* … */
};
[/code]

to

[code language=”java”]
class Customer {

private Date generationTimestamp;
private Date modificationTimestamp;

private final String recordId = "102";
/* … */
};
[/code]

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:

[code language=”java”]
for (int j = 0; j < 34; j++) {
s += (t[j] * 4) / 5;
}
[/code]

Với

[code language=”java”]
int realDaysPerIdealDay = 4;
const int WORK_DAYS_PER_WEEK = 5;
int sum = 0;
for (int j = 0; j < NUMBER_OF_TASKS; j++) {
int realTaskDays = taskEstimate[j] * realDaysPerIdealDay;
int realTaskWeeks = (realdays / WORK_DAYS_PER_WEEK);
sum += realTaskWeeks;
}
[/code]

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.

[code language=”java”]
public class Part {

private String m_dsc; // The textual description

void setName(String name) {
m_dsc = name;
}
}
[/code]

________________________________________________

[code language=”java”]
public class Part {

String description;

void setDescription(String description) {
this.description = description;
}
}
[/code]

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.

Không nên dùng tiếng lóng

Nếu những cái tên là quá đặc trưng, thường chúng sẽ được ghi nhớ với người chia sẻ cảm nhận vui vẻ và chỉ những người này mới nhớ những câu chuyện đó. Liệu chúng ta có biết hàm được đặt tên HolyHandGrenade hỗ trợ làm gì không? Chắc chắn hàm này là một tên đặc trưng nhưng có lẽ trong trường hợp này nên đặt tên DeleteItems có thể sẽ tốt hơn.

Sự lựa chọn tường minh vẫn hơn là các giá trị giải trí. Việc đặt tên quá đặc trưng trong đoạn mã thường xuất hiện trong các trường hợp thân mật hoặc tiếng lóng. Ví dụ, đừng sử dụng tên whack() để giải nghĩa là kill(). Không nên dùng tên địa phương khi nói chuyện như eatMyShort() để giải nghĩa abort(). Hãy nói những gì bạn thấy có nghĩa. Sự có nghĩa ở đây chính là những điều bạn nói.

Chọn Một Từ cho mỗi Khái Niệm

Hãy chọn một từ cho mỗi khái niệm trừu tượng và gắn nó vào. Ví dụ, các phương phức tương đương của các lớp khác nhau có những tên fetch, retrieve và get. Làm sao bạn có thể nhớ được tên của phương thức trong mỗi lớp? Thật buồn, bạn vẫn thường phải nhớ công ty, nhóm hoặc cá nhân nào đã viết thư viện hoặc lớp đó để biết tên nào được sử dụng, nếu không bạn phải mất thời gian đáng kể để tìm kiếm.

Những IDE hiện đại như Eclipse và IntelliJ cung cấp những gì có thể dùng được trong hoàn cảnh đó, ví dụ như danh sách phương thức có thể gọi trên một đối tượng nào đó. Nhưng chú ý rằng, danh sách này không thường xuyên cho chúng ta những chú thích mà bạn đã viết xung quanh tên phương thức, danh sách tham số. Bạn thực là may mắn nếu IDE cho bạn biết tên của của tham số của phương thực. Tên phương thức phải độc lập và thống nhất để bạn có thể chọn đúng phương thức mà không cần phải tìm kiếm gì thêm.

Tương tự, việc có controller, manager và driver trong cùng một mã nguồn sẽ gây bối rối.

Đâu là sự khác biện căn bản giữa DeviceManager và Protocol-Controller? Tại sao cả hai không mang tên controller hoặc manager? Cả hai có thực sự là Driver? Tên làm bạn nghĩ rằng đó hai đối tượng của hai loại rất khác nhau cũng như hai lớp khác nhau.

Một thuật ngữ nhất quán đem lại lợi ích rất lớn cho những ai sử dụng mã nguồn của bạn.

Không chơi chữ

Hãy tránh việc dùng một từ cho hai mục đích. Sử dụng một thuật ngữ cho hai ý tưởng khác nhau cơ bản là một trò chơi chữ.

Nếu bạn tuân thủ quy tắc “một từ một khái niệm”, thì bạn làm cho nhiều lớp có, ví dụ, một phương thức add. Không quan trọng giá trị trả về hoặc tham số đầu vào nhiều như thế nào, nhưng những phương thức add có ý nghĩa tương đương.

Tuy nhiên bạn nên cân nhắc việc dùng từ add vì lý do “nhất quán”  khi bạn thực sự không thêm vào theo cùng nghĩa. Giả sử rằng chúng ta có  phương thức add ở nhiều lớp, là tạo một giá trị mới bằng cách thêm vào hoặc nối hai giá trị có sắn. Bây giờ chúng ta viết một lớp mới mà có phương thức để cho một tham số vào một tập. Chúng ta có nên gọi nó là add? Có vẻ sẽ là nhất quán bởi chúng ta đã có nhiều phương thức tên là add, nhưng trong trường hợp này thì ý nghĩa là khác, bởi thế chúng ta nên dùng tên là insert hoặc append thì tốt hơn. Nếu gọi tên phương thức mới này là add thì đó là một trò chơi chữ.

Mục đích của chúng tôi, như những tác giả, là làm cho mã nguồn nguồn dễ hiểu nhất có thể. Chúng tôi muốn mã nguồn của mình có thể lướt nhanh, chứ không phải một nghiên cứ nặng nề. Chúng tôi muốn dùng mẫu bìa giấy phổ biến nơi tác giả có trách nhiệm tự làm rõ chứ không phải mẫu hàn lâm nơi công việc của trường học là đào sâu ý nghĩa.

Dùng tên của miềm giải pháp

Nên nhớ rằng những người đọc mã của bạn là lập trình viên. Nên hãy dùng những thuật ngữ của khoa học máy tính, tên trong giải thuật, tên mẫu thiết kế, tên toán học, v.v. Sẽ là không hay lắm nếu mọi tên đều là của miền nghiệp vụ bởi chúng ta không muốn những đồng nghiệp của mình phải chạy và ép hỏi khách hàng ý nghĩa của mỗi tên khi họ biết khái niệm đó bằng tên khác.

Tên accountVisitor có ý nghĩa rất rõ ràng với lập trình viên đã biết mẫu thiết kế VISITOR.  Có lập trình viên nào không biết JobQueue nghĩa là gì? Có rất nhiều thứ kỹ thuật mà lập trình viên phải biết. Việc chọn những tên kỹ thuật cho những thứ này thường là hợp lý nhất.

Dùng tên miền nghiệp vụ

Khi bạn đang làm với những thứ không phải của lập trình viên (“programmer-eese”), hãy sử dụng tên nghiệp vụ. Ít nhất lập trình viên bảo trì mã của bạn có thể hỏi chuyên gia nghiệp vụ về nghĩa của nó.

Tách những khái niệm nghiệp vụ và giải pháp là một phần của việc của một lập trình viên và thiết kế tốt. Mã mà cần phải quan tâm tới những khái niệm nghiệp vụ nên mang tên của nghiệp vụ.

Thêm bối cảnh có ý nghĩa

Có rất ít tên mà tự thân có ý nghĩa, hầu hết là không. Bạn cần đặt tên vào bối cảnh, bằng việc đặt chúng vào lớp, hàm, gói có tên rõ nghĩa để giúp người đọc hiểu. Khi bạn không làm được những điều này thì có thể dùng tiền tố như giải pháp cuối cùng.

Hãy tưởng tượng bạn có các biến với tên như sau: firstName, lastName, street, houseNumber, city, state và zipcode. Đặt chúng cạnh nhau thì rất rõ là sẽ tạo thành một địa chỉ. Nhưng biến state có nghĩa gì nếu bạn thấy nó một mình trong một phương thức? Liệu bạn có hiểu ngay đó là một phần của một địa chỉ?

Bạn có thể thêm bối cảnh bằng cách sử dụng tiền tốt: addrFirstName, addrLastName, adddrState, v.v. Ít nhất người đọc sẽ hiểu rằng những biến này là phần của một cấu trúc lớn hơn. Dĩ nhiên, giải pháp tốt hơn là tạo một lớp có tên là Address. Lúc đó thì thậm chí trình biên dịch cũng biết là những biến này thuộc một câu trúc lớn hơn.

Hãy xem phương thức ở mã dẫn 2-1. Liệu những biến này cần bối cảnh có ý nghĩa hơn? Tên của phương thức chỉ cung cấp một phần của bối cảnh; thuật toán cung cấp phần còn lại. Một khi bạn đọc hết phương thức, bạn thấy ba biến number, verb và pluralModifier là phần của thông báo “guess statistics”. Thật không may là chúng ta phải phỏng đoán bối cảnh. Lần đầu khi bạn nhìn phương thức,nghĩa của những biến này là không rõ ràng.

[code language=”java”]
private void printGuessStatistics(char candidate, int count) {
String number;
String verb;
String pluralModifier;
if (count == 0) {
number = "no";
verb = "are";
pluralModifier = "s";
} else if (count == 1) {
number = "1";
verb = "is";
pluralModifier = "";
} else {
number = Integer.toString(count);
verb = "are";
pluralModifier = "s";
}
String guessMessage = String.format(
"There %s %s %s%s", verb, number, candidate, pluralModifier
);
print(guessMessage);
}
[/code]

Mã dẫn 2-1: Biến trong bối cảnh không rõ ràng.

Phương thức hơi dài và biến được dùng từ đầu tới cuối. Để chia hàm ra thành nhỏ hơn, chúng ta cần tạo lớp GuessStatisticsMessage và để ba biến này thành thuộc tính của lớp này. Điều này tạo ra một bối cảnh rõ ràng cho cả ba biến. Chúng rõ ràng là phần của GuessStatisticsMessage. Sự cải thiện về bối cảnh này cũng cho phép thuật toán rõ ràng hơn bằng cách chia nhỏ thành những hàm nhỏ hơn (Xem mã dẫn 2-2).

[code language=”java”]
public class GuessStatisticsMessage {

private String number;
private String verb;
private String pluralModifier;

public String make(char candidate, int count) {
createPluralDependentMessageParts(count);
return String.format(
"There %s %s %s%s",
verb, number, candidate, pluralModifier);
}

private void createPluralDependentMessageParts(int count) {
if (count == 0) {
thereAreNoLetters();
} else if (count == 1) {
thereIsOneLetter();
} else {
thereAreManyLetters(count);
}
}

private void thereAreManyLetters(int count) {
number = Integer.toString(count);
verb = "are";
pluralModifier = "s";
}

private void thereIsOneLetter() {
number = "1";
verb = "is";
pluralModifier = "";
}

private void thereAreNoLetters() {
number = "no";
verb = "are";
pluralModifier = "s";
}
}
[/code]

Mã dẫn 2-2: Biến có bối cảnh.

Đừng thêm những bối cảnh vô căn cứ

Trong ứng dụng tưởng tượng có tên “Gas Station Deluxe”, việc thêm tiền tố GSD vào tên các lớp là một ý tưởng tồi. Thành thật mà nói, bạn đang làm việc chống lại công cụ của chính mình. Khi bạn gõ G và nhấn phím để hoàn thành, thì bạn nhận được một danh sách rất dài các lớp trong hệ thống. Đó là việc làm khôn ngoan? Tại sao lại làm khó cho việc IDE giúp bạn?

Tương tự, khi bạn tạo lớp MailingAddress trong mô-đun kế toán (accounting) của GDS, bạn đặt tên lớp là GSDAccountAddress. Sau đó, bạn cần một địa chỉ thư cho ứng dụng danh bạ khách hàng. Bạn có sử dụng GSDAccountAddress? Liệu đó có phải là một tên đúng? Mười trong 17 ký tự là thừa và không có ý nghĩa.

Tên ngắn thường tốt hơn tên dài, nếu chúng rõ ràng. Việc không thêm bối cảnh là cần thiết.

accountAddress và customerAddress là tên tốt cho những thể hiện của lớp Address, nhưng lại là tên lớp tồi. Address là tên lớp tốt. Nếu tôi cần phân biệt giữa địa chỉ MAC, địa chỉ cổng và địa chỉ Web, thì tôi nên xem xét PostalAddress, MAC và URI. Những tên này chính xác hơn.

Lời cuối

Việc khó nhất khi đặt tên bởi nó yêu cầu kỹ năng mô tả tốt và một nền văn hóa chia sẻ. Đó là việc dạy hơn là vấn đề kỹ thuật, kinh doanh hoặc quản lý. Kết quả là, nhiều người trong lĩnh vực này không học để làm rất tốt.

Mọi người thường sợ việc đổi tên bởi họ lo lắng là một số nhà phát triển khác sẽ đánh giá. Chúng tôi không khuyến khích sự sợ hãi này và thực tế thì chúng ta được mang ơn khi đổi tên (theo hướng tốt hơn). Chúng ta sử dụng công cụ hiện đại để giải quyết những tiểu tiết để từ đó có thể tập trung vào liệu việc đoạn mã có giống như đoạn và câu không, hoặc ít nhất cũng giống như bảng và cấu trúc dữ liệu (Không phải lúc nào một câu cũng là cách tốt nhất để hiển thị dữ liệu). Có thể bạn làm ai đó ngạc nhiên khi đổi tên, giống như những cải tiến mã nguồn khác. Đừng để nó cản đường bạn.

Hãy làm theo những quy tắc này và xem liệu bạn có cải tiến được mã nguồn của mình dễ đọc hơn. Nếu bạn bảo trì mã nguồn của người khác, hãy dùng công cụ tái câu trúc để giúp giải quyết những vấn đề này. Nó sẽ trả hết vốn trong thời gian ngắn và tiếp tục cho lãi về lâu dài.

Nguồn: Chương 2. Meaningful Name từ Clean Code: A Handbook of Agile Software Craftsmanship của Robert Martin

Người dịch Phạm Anh Đới


Hãy tham gia nhóm Học lập trình để thảo luận thêm về các vấn đề cùng quan tâm.

3 thoughts on “Mã sạch: Tên có ý nghĩa

  1. Bài viết hay quá thầy ơi, hóng chap 3 của thầy 😀 Hình như đoạn format code ví dụ có vấn đề thầy ạ 😀

  2. bài viết rất hay và rất có ý nghĩa
    bài viết đã giúp người đọc và người lập trình nên làm thế nào để đặt tên cho các hàm , phương thức, các biến cho người khác có thể hiểu một cách dễ dàng.

Leave a Reply

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

TÀI LIỆU DEVWORLD
Cẩm nang phát triển nghề nghiệp cho lập trình viên!