clean code tiếng việt chap 2

Giới thiệu

Trong phần mềm, chúng ta đặt tên cho các biến, các hàm, các đối số, lớp, tên cho các thư mục của chúng. Vì chúng ta phải đặt rất nhiều tên, nên chúng ta cần thực hiện tốt điều đó. Dưới đây là một số cách đơn giản để làm tốt điều đó trong clean code.

Sử dụng tên đúng ý nghĩa (Use Intention-Revealing Names)

Điều này đơn giản là để bộc lộ ý nghĩa qua tên gọi. Chọn một cái tên cần thời gian nhưng để nhớ nó còn tốn thời gian hơn nhiều. Vì vậy, hãy quan tâm đến cái tên mà bạn chọn và chỉ thay đổi chúng khi bạn sáng tạo ra tên tốt hơn.

Tên của biến, hàm, hoặc lớp phải trả lời tất cả những câu hỏi về nó. Nó phải cho bạn biết lý do nó tồn tại, nó làm được những gì, và dùng nó ra sao. Nếu có một comment đi kèm tên, thì tên đó không thể hiện được mục đích của nó.

int d;  // elapsed time in days

Tên d nó không liên quan gì đến ngày. Chúng ta nên chọn một tên thể hiện được những gì đang được cân đo, và cả đơn vị đo của chúng:

int elapsedTimeInDays;

int daysSinceCreation;

Việc chọn tên thể hiện đúng mục đích có thể làm cho việc hiểu và thay đổi code dễ dàng hơn.

Ví dụ:

public List<int[]> getThem() {

   List<int[]> list1 = new ArrayList<int[]>();

      for (int[] x : theList)

         if (x[0] == 4)

            list1.add(x);

   return list1;

}

Đoạn code trên bắt chúng tôi phải tìm câu trả lời cho các câu hỏi sau:

  1. theList chứa cái gì?
  2. Ý nghĩa của chỉ số 0 trong phần tử của theList?
  3. Số 4 có ý nghĩa gì?
  4. Danh sách được return thì dùng kiểu gì?

Chúng tôi thể cải thiện mã nguồn bằng cách đưa ra các khái niệm:

public List<int[]> getFlaggedCells() {

   List<int[]> flaggedCells = new ArrayList<int[]>();

      for (int[] cell : gameBoard)

         if (cell[STATUS_VALUE] == FLAGGED)

            flaggedCells.add(cell);

   return flaggedCells;

}

public List<Cell> getFlaggedCells() {

   List<Cell> flaggedCells = new ArrayList<Cell>();

      for (Cell cell : gameBoard)

         if (cell.isFlagged())

            flaggedCells.add(cell);

   return flaggedCells;

}

Tránh sai lệch thông tin (Avoid Disinformation)

Các lập trình viên phải tránh đặt những gợi ý sai lầm làm lu mờ ý nghĩa của code làm cho nó trở nên khó hiểu. Ví dụ như viết tắt từ này có thể hiểu nhầm sang nghĩa khác.

Ví dụ: Đừng quy một nhóm tài khoản thành accountList nếu nó không thực sự đây là một danh sách, vì có thể làm hiểu sai ý nghĩa. Nên thay bằng accountGroup, bunchOfAccount hay chỉ đơn giản là accounts.

Cẩn thận với những cái tên gần giống nhau. Mất bao lâu để bạn phân biệt được sự khác nhau giữa XYZControllerForEfficientHandlingOfStrings và XYZControllerForEfficientStorageOfStrings trong cùng một module.

clean code chap 2
clean code chap 2

Những cái tên gần giống nhau như thế này thật sự, thật sự rất khủng khiếp cho lập trình viên.

Một kiểu khủng bố tinh thần khác về những cái tên không rõ ràng là ký tự L viết thường và O viết hoa.

int a = l;

if ( O == l ) a = O1;

else l = 01;

Làm cho sự riêng biệt trở có nghĩ (Make Meaningful Distinctions)

Các lập trình viên tự tạo ra vấn đề cho chính họ khi viết code chỉ để đáp ứng cho trình biên dịch hoặc thông dịch. Ví dụ: Vì bạn không thể tạo cùng một tên để tham khảo đến 2 điều khác nhau trong cùng một phạm vi, bạn có thể bị “dụ dỗ” để thay đổi một tên cho phù hợp một cách tùy tiện. Đôi khi điều này được thực hiện bởi một lỗi chính tả, dẫn đến khu vực sửa lỗi chính tả và làm trình biên dịch không thể biên dịch.

Nếu phải đặt một tên khác, nên lựa chọn tên dựa vào sự khác nhau giữa 2 điều đó.

Những tên dạng chuỗi số (a1, a2,… aN) nó mâu thuẫn với nguyên tắc đặt tên có mục đích. Mặc dù những tên như vậy không phải là không đúng, nhưng chúng không có thông tin.

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

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

Những từ gây nhiễu tạo nên sự khác biệt vô nghĩa. Giả sử bạn có một class Product. Nếu bạn gọi hàm ProductInfo hoặc ProductData, thì bạn đã thành công trong việc tạo ra các tên khác nhau nhưng về mặt ngữ nghĩa thì chúng là một. Info và Data là các từ gây nhiễu, giống như a, an và the.

Những từ gây nhiễu là không cần thiết. Từ variable sẽ không bao giờ xuất hiện trong tên biến, từ table cũng không nên dùng trong tên bảng. Tên NameString tốt hơn Name ở chỗ nào?

Sử dụng tên đọc được (Use Pronounceable Names)

Con người rất giỏi về từ ngữ. Một phần quan trọng trong bộ não của chúng ta được dành riêng cho các khái niệm về từ. Và các từ, có thể phát âm được. Thật lãng phí khi không sử dụng được bộ não mà chúng ta đã tiến hóa nhằm thích nghi với ngôn ngữ nói. Vậy nên, hãy làm cho những cái tên phát âm được đi nào.

Hãy so sánh:

class DtaRcrd102 {

   private Date genymdhms;

   private Date modymdhms;

   private final String pszqint = “102”;

   /* … */

};

class Customer {

   private Date generationTimestamp;

   private Date modificationTimestamp;;

   private final String recordId = “102”;

   /* … */

};

Rõ ràng, đoạn mã được phiên âm rõ ràng thì người sử dụng sẽ dễ dàng hiểu được vấn đề hơn.

Sử dụng tên tìm kiếm được (Use Searchable Names)

Các tên một chữ cái và các hằng số có một vấn đề là không dễ dàng gì để xác định được vị trí của chúng.

Nếu một biến hoặc hằng số được sử dụng ở nhiều vị trí trong đoạn mã thì nên đặt cho nó một tên dễ tìm kiếm. Ví dụ:

for (int j=0; j<34; j++) {

   s += (t[j]*4)/5;

}

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;

}

Lưu ý rằng sum ở ví dụ trên, dù không phải là một tên đầy đủ nhưng có thể tìm kiếm được.

Tránh việc mã hóa (Avoid Encodings)

Các mã hóa hiện tại là đủ dùng vì vậy không cần bổ sung thêm vào. Mã hóa các kiểu dữ liệu hoặc phạm vi thông tin vào tên chỉ đơn giản là thêm một gánh nặng cho việc giải mã. Điều đó là không hợp lý khi yêu cầu nhân viên mới đọc hiểu những mã hóa lạ thêm vào trong khi những mã hóa có sẵn đã đủ dùng.

Ký pháp Hungary (Hungarian Notation):

Ngày trước, khi chúng tôi làm việc với những ngôn ngữ mà độ dài tên là một thách thức, chúng tôi đã loại bỏ sự cần thiết này. Fortran buộc phải mã hóa bằng cách thao tác với những chữ cái đầu tiên. Phiên bản đầu tiên của BASIC chỉ cho phép đặt tên tối đa 6 ký tự. Hungarian Notation đã giúp ích cho việc đặt tên rất nhiều.

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

  •  Thành phần tiền tố  (Member Prefixes)

Bạn cũng không cần phải thêm các tiền tố như m_ vào biến thành viên (member variable) nữa. Các lớp và hàm phải đủ nhỏ để không cần chúng. Bạn nên dùng các công cụ chỉnh sửa để đánh dấu hoặc bôi đậm các lớp hoặc các hàm để phân biệt chúng.

public class Part {

   private String m_dsc; // The textual description

   void setName(String name) {

      m_dsc = name;

   }

}

_________________________________________________

public class Part {

   String description;

   void setDescription(String description) {

      this.description = description;

   }

}

Bên cạnh đó mọi người còn bỏ qua các tiền tố hoặc hậu tố để tên có ý nghĩa hơn.

  • Interfaces and Implementations

Có một số trường hợp đặc biệt cần mã hóa. Ví dụ bạn đang xây dựng một Abstract Factory, Factory được tạo ra dưới dạng một Interface và được thực thi bởi một class cụ thể. (Class : Interface) Có thể đặt tên như thế nào?

Tôi thích dùng những cách đặt tên đơn giản. Trước đây, I rất phổ biến trong các tài liệu, nó làm chúng tôi phân tâm và đưa ra quá nhiều thông tin. Tôi không muốn người dùng biết rằng tôi đang tạo cho họ một giao diện, tôi chỉ muốn họ biết rằng đó là ShapeFactory. Vì vậy, nếu phải lựa chọn việc mã hóa hay thể hiện đầy đủ, tôi sẽ chọn cách thứ nhất. Gọi nó là ShapeFactoryImp, hoặc thậm chí là CShapeFactory là cách hoàn hảo để che giấu thông tin.

Tránh ánh xạ tinh thần (Avoid Mental Mapping)

Những lập trình viên khác sẽ không cần phải điên đầu ngồi dịch các tên mà bạn đặt thành những tên mà họ biết.

Chắc chắn bộ đếm vòng lặp có thể được đặt tên là i, j, k (không bao giờ là l-dĩ nhiên) nếu phạm vi của nó rất nhỏ và không có tên nào xung đột với nó. Điều này là do việc đặt tên có một chữ cái trong vòng lặp đã trở thành truyền thống. Tuy nhiên trong hầu hết trường hợp tên một chữ cái không phải là sự lựa chọn tốt.

Nói chung, lập trình viên là những người khá thông minh. Và những người thông minh đôi khi muốn thể hiện điều đó bằng cách hack não người khác.  

Sự khác nhau giữa lập trình viên thông minh và lập trình viên chuyên nghiệp đó là lập trình viên chuyên nghiệp hiểu được sự rõ ràng là trên hết. Lập trình viên chuyên nghiệp dùng khả năng của họ để viết Code làm cho người khác có thể hiểu được.

Tên lớp (Class Names)

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

Tên phương thức (Method Names)

Tên của Method nên là một độ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 với giá trị của chúng và tiền tố như get, set, is.

string name = employee.getName();

customer.setName(“mike”);

if (paycheck.isPosted())…

Khi Constructors overload, sử dụng phương thức tĩnh (static factory method ) cùng với tên và đối số. Ví dụ:

Complex fulcrumPoint = Complex.FromRealNumber(23.0);

Sẽ tốt hơn là:

Complex fulcrumPoint = new Complex(23.0);

Đừng tỏ ra dễ thương (Don’t Be Cute)

Việc lựa chọn những cái tên rõ ràng sẽ tốt hơn là một cái tên tỏ ra hóm hỉnh. Một lựa chọn tường minh thường tốt hơn là một giá trị mang tính chất giải trí.

Say what you mean (Hãy nói những gì bạn thấy có ý nghĩa). Mean what you say. 

Chọn một từ cho mỗi khái niệm (Pick One Word per Concept )

Chọn một từ cho một khái niệm và gắn bó với nó. Ví dụ, rất khó hiểu khi fetch, retrieve và get là các phương thức có cùng chức năng, nhưng lại đặt tên khác nhau ở các lớp khác nhau. Làm thế nào để nhớ phương thức nào đi với lớp nào? nếu không sẽ mất khá nhiều thời gian cho việc tìm kiếm.

Các công cụ chỉnh sửa(IDE) hiện đại hiện đại hỗ trợ chức năng tìm kiếm và thêm các tham số cần thiết. Tên chức năng phải đứng một mình, và nó phải phù hợp để các bạn đúng phương pháp mà không thăm dò bổ sung. Tên hàm phải đứng một mình, và chúng phải nhất quán để bạn có thể chọn đúng phương pháp mà không cần phải tìm hiểu thêm.

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.

Đừng chơi chữ (Don’t Pun)

Tránh dùng cùng một từ cho hai mục đích. Sử dụng cùng một thuật ngữ cho hai ý tưởng khác nhau đơn giản đó chỉ 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“, bạn có thể sử dụng nó cho nhiều class. Tuy nhiên bạn cần cân nhắc khi vì lý do nhất quán nhưng phương thức bạn sử dụng lại không có cùng nghĩa.

Ví dụ, phương thức add. Miễn là danh sách tham số và giá trị trả về của các phương thức add này tương đương về ý nghĩa, tất cả đều tốt.

 Tuy nhiên cần cân nhắc sử dụng phương thức add bạn định thêm vào không cùng ngữ nghĩa, có thể hiểu nhầm sang insert hoặc append. Giả sử chúng ta có nhiều classes với phương thức add, sẽ tạo một giá trị mới bằng cách thêm vào hoặc nối 2 giá trị có sẵn. Bây giờ chúng ta tạo một class mới với phương thức đặt một tham số vào một tập hợp. Lúc này không thể đặt tên phương thức là add nữa vì nó đã không còn  phù hợp. trong trường hợp này chúng ta dùng insert hoặc append thì phù hợp hơn. Đặt tên phương thức là add lúc này một trò chơi chữ.

Thêm bối cảnh có ý nghĩa (Add Meaningful Context )

Chỉ có một vài cái tên có nghĩa trong mọi trường hợp – số còn lại thì không. Vậy nên, bạn cần đặt tên phù hợp với ngữ cảnh.

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. Khi kết hợp với nhau, chúng rõ ràng tạo thành một địa chỉ. Nhưng nếu bạn chỉ thấy biến state được sử dụng một mình trong một phương thức thì sao? Bạn có thể suy luận ra đó là một phần của địa chỉ không?

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. Tất nhiên, giải pháp tốt hơn là tạo một lớp có tên là Address. Khi đó, ngay cả trình biên dịch cũng biết rằng các biến đó thuộc về một khái niệm lớn hơn.

Lời kết

Điều khó khăn nhất của việc lựa chọn tên đẹp là nó đòi hỏi kỹ năng mô tả tốt và nền tảng văn hóa lớn.

Đó là việc dạy nhau, học hỏi nhau 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 cách để làm điều đó.

Bài viết cùng chủ đề:

  1. Clean code tiếng việt Chap 1
  2. Clean code tiếng việt chap 4
  3. Clean code tiếng việt chap 6 – ĐỐI TƯỢNG VÀ CẤU TRÚC DỮ LIỆU

Bình luận