Clean code series: Part 2 - Hướng dẫn đặt tên biến

Những cái tên có ở khắp mọi nơi trong việc phát triển phần mềm. Chúng ta đặt tên cho các variable, function, argument, class, và package. Chúng ta đặt tên cho source files và folder chứa chúng. Vì chúng ta cần đặt tên rất nhiều nên tốt hơn chúng ta cần làm tốt nó. Sau đây là một vài quy tắc cơ bản giúp chúng ta tạo ra Những cái tên tốt.

Sử dụng những cái tên thể hiện rõ mục đích.

Điều này hầu hết mọi người đều biết nhưng nó chưa thực sự được mọi người quan tâm đúng mức. Việc đặt Những cái tên thể hiện rõ mục đích có thể khiến bạn mất nhiều thời gian nhưng sau đó nó sẽ giảm thời gian để các thành viên đọc hiểu code của bạn và phát triển hoặc sửa chữa nó. Điều này thật sự quan trọng với việc hoạt động teamwork.

Tên của variable, function hoặc class cần trả lời được những câu hỏi quan trọng:

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

Nếu cần có một comment để giải thích nó thì tên đó chưa thể hiện được được mục đích của nó.

int d;   // elapsed time in days

Biến d không giúp chúng ta hình dung được mục đích của biến này để làm gì, và sử dụng như thế nào. Nếu muốn hiểu ý nghĩa của nó bắt buộc phải tìm phần khai báo biến và xem comment của code, nhưng nếu như ta đặt:

int elapsedTimeInDays;

Biến elapsedTimeInDays giúp ta hình dung ý nghĩa và mục địch của nó.
Ví dụ về việc đặt biến trong 1 funtion:

public function getThem($theList) {
    $list1 = [];
    foreach ($theList as $list) {
            if ($list[0] == 4) {
                array_push($list1, $list);
            }
        }
    return $list1;
}

Nếu như chúng ta sử dụng tên biến như trên syntax code và coding convention đều được tuân thủ nhưng vẫn rất khó để chúng ta hiểu mục đích và ý nghĩa của function getThem() là gì?
Đoạn code trên để hiểu chúng ta cần phải trả lời được 4 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ì?

Giả sử chúng ta sử dụng đoạn code này trong game dò mìn. Phần theList là mảng chứa các ô vuông (cell). Chỉ số 0 là vị trí biểu diễn giá trị trang thái của ô, và giá trị 4 nghĩa là trạng thái được gắn cờ (flagged). Dựa vào những dữ liệu trên chúng ta viết lại function này mà vẫn giữ nguyên chức năng của nó:

const STATUS_VALUE = 0;
const FLAGGED = 4;

public function getFlaggedCells($gameBoard) {
    $flaggedCells = [];
    foreach ($gameBoard as $cell) {
            if ($cell[STATUS_VALUE] == FLAGGED) {
                array_push($flaggedCells, $cell);
            }
        }
    return $flaggedCells;
}

Khi ta thay đổi cách đặt tên biến rõ dàng function getThem() đã dễ hiểu hơn rất nhiều và độ phức tạp thuật toán cũng như syntax code và coding convention đều được đảm bảo.

Chúng ta có thể tiếp tục cải thiện code bằng cách thêm một lớp đơn giản cho các ô thay vì sử dụng mảng. Class cell thêm một function isFlagged giúp kiểm tra xem ô đó có phải là falgged hay không:

public function getFlaggedCells($gameBoard) {
    $flaggedCells = [];
    foreach ($gameBoard as $cell) {
            if ($cell.isFlagged()) {
                array_push($flaggedCells, $cell);
            }
        }
    return $flaggedCells;
}

Với những sự thay đổi đơn giản bằng cách thay đổi tên, không quá khó để thực hiện đã giúp source code trở lên dễ hiểu và giúp các thành viên trong team dễ dàng hiểu logic của project.

Tránh sai lệch thông tin.

Các developer cần phải trách để lại những dấu hiệu làm code trở nên khó hiểu hơn. Chúng ta nên tránh dùng những từ mang nghĩa khác với nghĩa cố định của nó có thể dẫn tới nhầm lẫn cho người đọc.
Ví dụ:

  • Chúng ta không nên đặt một nhóm tài khoảng là một accountList nếu nó không thật sự là một List. Nếu các tài khoản không thực sự tạo thành 1 list thì nên đặt tên là accountGroup hoặc accounts sẽ tốt hơn.

  • XYZControllerForEfficientHandlingOfStrings và XYZControllerForEfficientStorageOfStrings bạn có thể phân biệt luôn nếu nhìn thấy chúng không? nhưng tên như thế này rất dễ nhầm lẫn cho các developer.

Tạo nên những sự khác biệt có nghĩa.

Cẩn trọng với suggest tên của trình editor có thể dẫn tới được đặt không thật sự liên quả và đủ ý nghĩa của bạn mong muốn.
Tránh sử dụng các từ đồng nghĩa gây hiểu nhầm và khó hiểu cho người đọc. Ví dụ ProductInfo và ProductData đã có sự khác biệt giữa 2 class nhưng thực tế về mặt ngữ nghĩa chúng là một và không biết khi nào dùng ProductData và khi nào dùng ProductInfo.
Tránh sử dụng các từ gây nhiễu không cần thiết ví dụ:

  • Thêm variable vào tên biến, vì dù bạn có không thêm họ vẫn hiểu tương tự như table cũng không nên sử dụng trong tên bảng.

  • NameString có tốt hơn Name không? Điều này là không cần thiết vì name bao giờ cũng là string, nếu là số thì nó đã phá vỡ nguyên tắc tránh sai lệch thông tin.

Trong trường hợp không có quy ước cụ thể, biến moneyAmount không thể phân biệt được với money; customerInfo không thể phân biệt được với customer; accountData không thể phân biệt được với account và theMessage với message được xem là một. Hãy phân biệt tên theo cách cung cấp cho người đọc những khác biệt rõ ràng.

Dùng những tên phát âm được.

Việc sử dụng các tên phát âm được giúp dễ dàng trao đổi giữa các thành viên trong nhóm. Điều này còn giúp việc tìm kiếm dễ dàng hơn, và dễ ghi nhớ nội dung code hơn.
Ví dụ:

class DtaRcrd102 {
 private $genymdhms;
 private $modymdhms;
 private $pszqint = "102";
 /* ... */
};

và

class Customer{
 private $generationTimestamp;
 private $modificationTimestamp;
 private $recordId= "102";
};

Dùng những tên tìm kiếm được.

Các tên một chữ cái ($e) và hằng số 5 luôn có vấn đề, đó là không dễ để tìm chúng trong hàng ngàn dòng code của project.
Nếu hằng số 5 được đặt tên là MAX_CLASSES_PER_STUDENT thì việc tìm kiếm MAX_CLASSES_PER_STUDENT trong project dễ dàng hơn rất nhiều. Điều đó còn giúp bạn tránh nhầm lẫn giữa các hằng số nhưng có mục đích khác nhau ví dụ như:

class STUDENT {
    const MAX_CLASSES_PER_STUDENT = 5;
    /* ... */
}

class TEACHER {
    const WORK_DAYS_PER_WEEK = 5;
}

Trong đoạn ví dụ trên cả 2 hằng số đều có giá trị là 5 nếu như không đặt tên cho chúng thì khi tìm kiếm rất khó phân ý nghĩa của chúng, ngoài ra nó còn giúp chúng ta chỉ cần update ở 1 chỗ cho toàn bộ project.

Tránh việc mã hóa.

Ký pháp Hungary

Trước kia việc đặt tên biến dài là một thách thức vì giới hạn số kí tự để đặt tên biến. Nhưng hiện nay điều đó đã không còn lên việc đặt tên bằng kí pháp Hungary là không cần thiết. Việc sử dụng ký pháp Hungary chỉ khiến cho việc đổi tên biến, tên hàm khó khăn hơn. Chúng làm cho code khó đọc, và tạo ra một hệ thống mã hóa có thể đánh lừa người đọc.

$phoneString = '';
// name not changed when type changed!

Các thành phần tiền tố

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à các hàm phải đủ nhỏ để bạn không cần chúng. Và bạn nên sử dùng các công cụ chỉnh sửa giúp làm nổi bật các biến này, làm cho chúng trở nên khác biệt với phần còn lại.

Interfaces và 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 sẽ là giao diện và sẽ được thực hiện bởi một lớp cụ thể. Bạn sẽ đặt tên cho chúng là gì?

abstract class ServiceProvider
/* ... */

class AppServiceProvider extends ServiceProvider

Việc đặt tên factory là AppServiceProvider đã là 1 cách hoàn hảo để che giấu thông tin trong ServiceProvider.

Tránh Mental Mapping

Hạn chế việc người đọc code phải dịch tên bạn đặt ra sang một tên khác mà họ biết. Vấn đề này có thể xảy ra khi bạn sử dụng những tên không nằm trong phạm vi của bài toán đặt ra, hoặc sử dụng tên khác với tư duy thông thường.Ví dụ rõ nhất là đặt tên biến chỉ có một chữ cái và sử dụng các hằng số magic. Ví dụ:

public static $secondsInADay = 24 * 60 * 60;

Với

const SECONDS_IN_A_MINUTE = 60;
const MINUTES_IN_AN_HOUR = 60;
const HOURS_IN_A_DAY = 24;
public static $secondsInADay = self::SECONDS_IN_A_MINUTE  * self::MINUTES_IN_AN_HOUR * self::HOURS_IN_A_DAY ;

Tên class

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ừ.
Ví dụ:

class Book;
class Color;

Tên Method

Tên cá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 cập, chỉnh sửa thuộc tính phải được đặt tên cùng với get, set và is.

getBackGrounds() // get background

setName() // set name

isFlagged() // check flagged

Đừng thể hiện rằng bạn cute

Không sử dụng các tên dùng từ nóng hay tiếng địa phương, hay chỉ nhóm bạn biết được. Ví dụ giữa whack() và kill() hay eatMyShorts() và abort thì kill(), abort rõ ràng và dễ hiểu hơn cho tất cả mọi người.

Chọn một từ cho mỗi khái niệm

Việc có nhiều từ có cùng một nghĩa với nhau thì trong project chúng ta cần thống nhất dùng 1 từ cho 1 hành động ví dụ: fetch, retrieveget đều mô tả một chức năng thì chúng ta cần thống nhất dùng get chẳng hạn. Khi đó bạn dễ dàng nhớ được method đi theo các class.

Đừng chơi chữ

Tránh dùng một từ cho nhiều mục đích. Việc dùng một từ cho nhiều mục khác nhau khiến chính bản thân bạn có thể nhầm lẫn chúng. Ví dụ như phương thức add() là thêm mới một đối tượng, tuy nhiên nếu chúng ta dùng nó add() với ý nghĩa nữa là cộng thì rất dễ nhầm lẫn giữa chúng. Mục tiêu của chúng ta là làm cho code dễ hiểu và mạch lạc nhất có thể, việc nhầm lẫn như trên sẽ rất tốn công đọc hiểu và tìm hiểu logic của code.

Dùng thuật ngữ

Việc đặt tên code của bạn là cho những lập trình viên, vậy nên hãy sử dụng các thuật ngữ khoa học, các thuật toán, tên mẫu cho việc đăt tên. Ví dụ chỉ cần nhìn tên class HomeController là bạn biết đây là một controler xử lý trang home page hay StoreBookRequest là bạn biết đây là class có nhiệm vụ validate.

Thêm ngữ cảnh thích hợp

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, bằng cách đặt chúng vào các lớp, các hàm hoặc các không gian tên
(namespace). Khi mọi thứ thất bại, tiền tố nên được cân nhắc như là giải pháp cuối cùng.
Hãy tưởng tượng bạn có các biến có tên là 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 địachỉ. 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ạncó thể suy luận ra đó là một phần của địa chỉ không?
Bạn có thể thêm ngữ cảnh bằng cách sử dụng tiền tố: addrFirstName, addrLastName,addrState,… Ít nhất người đọc sẽ hiểu rằng những biến này là một phần của một cấu trúc lớn hơn.Tất nhiên, một 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.
Tên ngắn thường tốt hơn tên dài, miễn là chúng rõ ràng. Thêm đủ ngữ cảnh cho tên sẽ tốt hơn khi cần thiết.

Lời kết

Qua bài blog này tôi mong muốn chúng ta quan tâm tới việc đăt tên giúp cho project rõ dàng và mạch lạc hơn. Từ đó có thể năng hiệu suất của mọi người giúp việc fixbug hay nhẩy vào dự án đang làm dở không còn là ác mộng với mọi người.

Tham khảo tại: https://enos.itcollege.ee/~jpoial/oop/naited/Clean Code.pdf