Làm thế nào để đặt tên có ý nghĩa

# Clean code - Code cũng là một nghệ thuật

Làm thế nào để đặt tên có ý nghĩa

Nội dung bài viết


Dịch từ: Sách Clean code - Robert C. Martin

Tên có ở mọi nơi trong code. Chúng ta đặt tên biến, đặt tên hàm, đặt tên tham số , đặt tên class... Chúng ta đặt tên source, đặt tên thư mục chứa chúng. Chúng ta đặt tên, đặt tên và đặt tên. Bởi vì chúng ta làm nó quá nhiều, chúng ta phải làm tốt nó. Theo dõi các nguyên tắc cơ bản dưới đây để tạo ra một cái tên tốt.

Sử dụng tên có ý nghĩa

Rất dễ dàng để gọi một tên có ý nghĩa. Chọn một tên tốt sẽ lấy nhiều thời gian nhưng nó sẽ tiết kiệm nhiều hơn là nó lấy. Bởi vì việc nâng cấp, bảo trì code sẽ rất khó khăn nếu chính bạn cũng không biết ý nghĩa của tên mà bạn đặt. Chính vì vậy bạn cần chăm sóc các tên mà bạn đặt và thay đổi chúng nếu tìm ra được một tên khác tốt hơn.

Một tên biến, hàm hoặc class nên trả lời được toàn bộ các câu hỏi lớn.

  • Tại sao nó tồn tại?
  • Nó dùng để làm gì?
  • Dùng nó như thế nào?

Nếu một tên mà cần chú thích để cung cấp thông tin thì đó không phải là một tên có ý nghĩa.

int d = 10; // Day of Birth

Tên biến d không mang lại ý nghĩa gì cả. Nó không gợi lên một cảm giác gì là ngày sinh cả, thậm chí là ngày. Chúng ta nên chọn một tên tốt hơn để làm cho nó trở nên có nghĩa.

int dayOfBirth = 10;

Đặt một tên có ý nghĩa sẽ giúp ta dễ dàng hiểu được và thay đổi code. Hãy nhìn xem mục đích của đoạn code này là gì?

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

Tại sao nó khó để cho ta biết đoạn code này đang làm gì? Nó không có các biểu thức phức tạp. Khoảng trắng và thụt lề hợp lý. Chỉ có 3 biến và 2 hằng số được đề cập. Không có bất kỳ class đặc biệt hay phương thức trừu tượng nào, chỉ có mảng (hoặc cái gì đó tương tự như vậy).

Vấn đề ở đây không phải là sự đơn giản của code mà là ý nghĩa của nó: ngữ cảnh của đoạn code thật sự không rõ ràng. Ý nghĩa của đoạn code trên yêu cầu phải cho chúng ta biết câu trả lời của các câu hỏi sau:

  1. theList là kiểu dữ liệu gì?
  2. Ý nghĩa của index 0 của một item trong theList?
  3. Ý nghĩa của số 4?
  4. Chúng ta sẽ sử dụng list1 như thế nào?

Đáp án cho các câu hỏi trên không nằm trong đoạn code ví dụ, nhưng chúng có thể có. Có thể nói rằng chúng ta đang làm một game gỡ mìn. Chúng ta duyệt bảng trò chơi như một danh sách các ô được gọi là theList. Hãy đổi tên thành gameBoard.

Mỗi ô trong bảng được đại diện bởi một mảng. Index 0 chính là vị trí của một giá trị trạng thái và với giá trị bằng 4 nghĩa là "đã cắm cờ". Chỉ cần cung cấp một vài khái niệm tên thì ta có thể tối ưu đoạn code như sau

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

Chú ý rằng sự đơn giản của đoạn code không thay đổi. Nó vẫn có đầy đủ số lượng toán tử và hằng số, cùng với cấp độ lồng của các khối code. Nhưng code đã trở nên rõ ràng hơn nhiều.

Chúng ta có thể tiếp tục và viết một class đơn giản cho ô bảng thay vì sử dụng mảng. Nó có thể bao gồm một hàm có ý nghĩa (gọi là isFlagged) để ẩn đi magic-number. Kết quả một phiên bản mới của hàm

public List<Cell> getFlaggedCells() {
  List<Cell> flaggedCells = new ArrayList<Cell>();
  for (Cell cell : gameBoard) {
    if (cell.isFlagged()) {
      flaggedCells.add(cell);
    }
  }
  return flaggedCells;
}

Với những sự thay đổi tên đơn giản, thật không khó để hiểu đang nó làm chuyện gì. Đây là sức mạnh của việc đặt tên tốt.

Tránh thông tin sai lệch

Lập trình viên nên tránh để lại các manh mối sai làm "mờ" đi nghĩa của code. Chúng ta nên tránh các từ làm lệch ý nghĩa dự định. Ví dụ như là hp, axi, sco là những tên biến "nghèo nàn" vì chúng khó hiểu và dễ nhầm lẫn thông tin. Chẳng hạn bạn đang code một cạnh huyền (hypotenuse) và tên hp trông có vẻ là một tên viết tắt tốt thì có lẽ bạn đã sai. Nó có thể đưa ra thông tin sai lệch.

Đừng đặt tên cho một nhóm tài khoản với tên accountList trừ khi thật sự là một List (kiểu dữ liệu trong Java). Nếu nó không thực sự chứa các tài khoản dưới kiểu List thì nó có thể dẫn đến kết luận sai. Vì thế, đặt tên là accountGroup hoặc bunchOfAccounts hoặc chỉ đơn giản là accounts thì sẽ tốt hơn.

Một ví dụ kinh khủng có thật cho việc đặt tên thông tin sai lệch là sử dụng chữ l thường và chữ O in hoa cho tên biến, đặt biệt khi kết hợp với nhau. Vấn đề ở đây là ta hầu như nhìn thấy tất cả trông như số 1 và số 0.

int a = 1;
if (O == l) 
  a = O1;
else
  l = 01;

Vấn đề này hầu như không còn "đánh đố" người đọc nữa vì các code font và highlight trong các IDE hiện nay đã giúp ta có thể dễ dàng phân biệt chúng.

Tạo sự khác biệt có ý nghĩa

Các lập trình viên tạo ra vấn đề cho chính họ khi mà họ viết code chỉ để thỏa mãn trình biên dịch. Ví dụ, bạn không thể sử dụng các tên tương tự cho hai thứ khác nhau trong cùng một phạm vi, bạn có thể bị cám dỗ để đổi tên một cách tùy ý. Chẳng hạn tạo một biến với tên klass chỉ vì tên biến class đã được sử dụng.

Nếu những cái tên khác nhau, thì chúng cũng nên có nghĩa khác nhau.

Cách đặt tên dãy số (a1, a2, ... aN) là đối ngược với cách đặt tên có chủ ý. Những cái tên này không phù hợp vì chúng không có thông tin. Chúng không cung cấp manh mối gì của ý định tác giả. Chẳng hạn

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

Hàm này đọc tốt hơn khi sourcedestination được sử dụng làm tên tham số.

Những từ gây nhiễu có sự khác biệt không mang ý nghĩa. Tưởng tượng bạn có một class Product. Nếu bạn có các class khác được gọi là ProductInfo hay ProductData, bạn đã tạo ra các tên khác nhau nhưng không tạo chúng có nghĩa khác nhau. InfoData là những từ gây nhiễu không rõ ràng như a, an, và the.

Chú ý rằng điều này không sai với việc sử dụng quy ước tiền tố như athe miễn là chúng tạo ra sự khác biệt có ý nghĩa. Ví dụ bạn có thể sử dụng a cho tất cả các biến toàn cục và the cho tất cả các tham số của hàm. Vấn đề chỉ xuất hiện khi bạn quyết định gọi một biến theZork chỉ vì bạn đã sử dụng tên biến zork rồi.

Từ gây nhiễu là dư thừa. Từ variable không nên bao giờ xuất hiện trong một tên biến. Từ table không nên bao giờ xuất hiện trong một tên bảng. NameString thì tốt hơn Name chỗ nào chứ? Một Name đã bao giờ là số thập phân chưa? Nếu vậy, nó sẽ phá vỡ đi nguyên tắc "tránh thông tin sai lệch".

Trong một số quy ước cụ thể, biến moneyAmount không thể phân biệt với money, customerInfo không thể phân biệt với customer, accountData không thể phân biệt với accounttheMessage không thể phân biệt với message. Hãy phân biệt tên theo cách mà người dùng biết được những gì sự khác biệt cung cấp.

Sử dụng tên phát âm được

Con người rất tốt về từ ngữ. Một phần ý nghĩa của não chúng ta dành cho khái niệm từ vựng. Và từ theo định nghĩa phải phát âm được. Sẽ thật xấu hổ nếu không tận dụng một phần lợi thế khổng lồ của não chúng ta đã được phát triển để đối phó với ngôn ngữ nói. Vì vậy hãy tạo tên có thể phát âm được.

Nếu bạn không thể phát âm nó, bạn không thể thảo luận về nó mà không nói, nghe có vẻ như một kẻ ngốc. Vấn đề bởi vì lập trình là một hoạt động xã hội.

Một công ty tôi biết có biến genymdhms (generation date, year, month, day, hour, minute, second) và họ đi vòng quanh thảo luận với nhau và nói "gen why emm dee aich emm ess". Tôi có một thói quen là phát âm những thứ được viết, vì vậy tôi bắt đầu đọc "gen-yah-mudda-hims". Sau đó nó được gọi bởi các nhà thiết kế máy chủ và nhà phân tích nhưng chúng tôi vẫn nghe có vẻ ngớ ngẩn. Nhưng chúng tôi đã ở trong một câu chuyện hài, vì vậy nó rất vui. Vui hay không, chúng tôi đã chịu đựng cách đặt tên "nghèo". Những lập trình viên mới nhận được sự giải thích về các biến, và sau đó họ phát âm về nó như một từ tạo nên từ sự ngớ ngẩn thay vì sử dụng các nguyên tắc tiếng Anh thích hợp. Hãy so sánh

class DtaRcrd102 {
  private Date genymdhms;
  private Date modymdhms;
  private final String pszqint = "102";
  /* ... */
}

với

class Customer {
  private Date generationTimestamps;
  private Date modificationTimestamps;
  private final String recordId = "102";
  /* ... */
}

Cuộc hội thoại thông minh bây giờ có thể là: "Hey, Mikey, hãy nhìn vào bản ghi này! Cái "generation timestamp" được đặt thành thời gian ngày mai! Làm thế nào có thể được?".

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

Những tên một ký tự và các hằng số có một vấn đề cụ thể là chúng không dễ dàng để định vị trong một văn bản.

Mọi người có thể dễ dàng tìm thấy MAX_CLASSES_PER_STUDENT, nhưng số 7 có thể rắc rối hơn. Nó thậm chí tệ hơn nếu nó là một con số dài. Bằng cách ấy có thể tạo ra một lỗi trong suốt quá trình tìm kiếm của lập trình viên.

Tương tự như vậy, tên e là một sự lựa chọn "nghèo nàn" cho bất kỳ biến nào để một lập trình viên có thể tìm kiếm. Nó là một ký tự thông dụng nhất trong ngôn ngữ tiếng Anh và gần như xuất hiện trong mỗi đoạn code của mỗi chương trình. Về vấn đề này, những tên dài "ăn" những tên ngắn, và bất kỳ tên tìm kiếm được nào đều "ăn" một hằng số trong code.

Sự ưa thích của riêng tôi là chỉ có thể sử dụng các tên một ký tự như là một biến cục bộ trong những phương thức ngắn.

Độ dài của một tên nên tương ứng với kích thước phạm vi của nó.

Nếu một biến hoặc một hằng có thể được thấy hoặc được sử dụng ở nhiều nơi trong code, bắt buộc cho nó một cái tên tìm kiếm thân thiện. Một lần nữa so sánh

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

với

int realDaysPerIdealDay = 4;
const int WORK_DAYS_PER_WEEK = 5;
int sum = 0;
for (int j = 0; j < NUMBER_OF_TAKSl j++) {
  int realTaskDays = taskEstimate[j] * realDaysPerIdealDay;
  int realTaskWeek = (realTaskDays / WORK_DAYS_PER_WEEK);
  sum += realTaskWeeks;
}

Chú ý biến sum ở trên, nó không phải là một tên cụ thể hữu ích nhưng ít nhất có thể tìm kiếm được. Việc đặt tên có chủ ý sẽ làm cho code một hàm dài hơn, nhưng hãy xem xét nó sẽ dễ dàng hơn bao nhiêu để tìm WORK_DAYS_PER_WEEK hơn việc tìm kiếm tất cả ở những nơi có số 5 và lọc đến khi tới chỗ code dự định.

Còn nữa

Bài này hơn dài rồi nên mình sẽ kết thúc tại đây, bài tiếp theo chúng ta sẽ tiếp tục phần còn lại của chủ đề "Đặt tên có ý nghĩa" này. Cảm ơn các bạn đã luôn đồng hành cùng Flipper, chúc các bạn học tốt và hẹn gặp lại.


Ủng hộ chúng tôi

Bình luận