[Code sao cho chuẩn] – Phần 4: Chúng ta nên định dạng code như thế nào?

    Xét ở một khía cạnh nào đó, việc viết code cũng giống như bạn viết tạp chí vậy. Khi viết tạp chí, để trải nghiệm người đọc được cải thiện và thu hút người đọc, chúng ta cần xắp xếp bố cục (layout) nội dung một cách tự nhiên và hợp lí. Bố cục này liên quan tới các vấn đề như: bài viết có bao nhiêu đoạn, mỗi đoạn có độ dài bao nhiêu, căn chỉnh thứ tự và vị trí mỗi đoạn như thế nào, hình ảnh sẽ chèn vào đâu ..v.v. Quá trình viết code trong phát triển phần mềm cũng tương tự như vậy, bởi vì: code ta viết ra sẽ có lúc được đọc (bởi ta hoặc người khác) để bảo trì và phát triển.

    Bởi vì, code viết ra không chỉ để cho máy hiểu, mà cũng cần phải để con người hiểu nữa. Thế nên, ngoài việc code có thể biên dịch được mà không lỗi, ta cũng cần chú ý tới định dạng và bố trí code như thế nào cho “thuận con mắt”. Nhìn vào source code có “thuận mắt” thì người ta mới có hứng thú mà phát triển tiếp được. Source code đơn thuần chỉ là văn bản (plain text), tuy nhiên, bằng cách sử dụng khoảng trắng hợp lí, độ dài mỗi dòng, sắp xếp vị trí …. ta có thể cải thiện “tính thẩm mĩ” của code lên rất nhiều.

format code 1.JPG
Bạn hiểu giá trị của sự ngăn nắp rùi chứ? (Nguồn: The art of readable code)

    Chúng ta đừng nhầm lẫn việc “định dạng code” với việc “thiết kế hệ thống“, hai việc này là 2 vấn đề hoàn toàn riêng biệt. Trong khi “thiết kế hệ thống” lên quan tới việc cấu trúc source, cách phân tách các lớp và module, phân tầng nhiệm vụ để hiện thực hoá tính năng, thì việc “định dạng code” đơn thuần chỉ làm nhiệm vụ làm cho code đẹp hơn, dễ đọc hơn, chỉnh chu hơn. Tất nhiên, khi bạn định dạng tốt, code của bạn sẽ tự nhiên hiệu quả hơn.

    Vậy làm thế nào để ta có thể định dạng code tốt hơn? Chúng ta sẽ tập trung vào 3 vấn đề chính sau đây:

  • Bố cục (layout) cần được sắp xếp một cách nhất quán , đề ra code conventions thống nhất.
  • Những đoạn có xử lí giống nhau thì nên có cách viết tương tự nhau.
  • Tách hàm cho những xử lí lặp lại nhiều lần.
  • Gom những đoạn code liên quan vào từng đoạn code nhỏ (gom thành những block).

    Khỏi phải nói, source code được format cẩn thận sẽ giúp ta làm việc dễ dàng hơn, và cũng làm ta cảm thấy thoải mái hơn rất nhiều. Chúng ta sẽ cùng xem xét những tình huống cụ thể của những vấn đề vừa được nêu ra ở trên, xem thử có điều gì chúng ta cần phải cải thiện trong source code của mình không nhé.

I. Bố cục và coding conventions phải thống nhất

    Khi bắt đầu viết code ở bất kì một project nào, điều đầu tiên bạn nên làm là kiểm tra những quy ước về cách viết code (coding conventions). Những thứ liên quan tới coding rules như: đặt tên biết theo kiểu camelCase hay là snack_case, canh dòng bằng dấu tab hay là khoảng trắng, nếu là khoảng trắng thì sẽ là 4 hay 6 khoảng trắng, có phải viết document cho hàm, …v.v..

   Hãy hình dung đoạn code sau đây:

class Employee{
   String full_name;
   int yearsOfExperience;
   date date_of_birth;

   void setName(_name){
      this->full_name = _name;
   }

   /*
   * @param
   * date _date
   */
   void set_date_of_birth(_date)
   {
      this->date_of_birth = _date;
   }
}

   Bạn có nhận thấy đoạn code trên có những vấn đề gì không? Ít nhất là chúng ta có thể chỉ ra ngay được 3 lỗi cơ bản sau: cách đặt tên biến không thống nhất – lúc thì camelCase lúc thì snack_case, document cho hàm không thống nhất – hàm có hàm không, dấu ngoặc nhọn “{}” đặt không đúng chỗ – dấu mở ngoặc “{” viết trên cùng dòng hay xuống dòng? Đó là chưa kể đến các lỗi có thể gặp phải như việc dùng lùi đầu dòng, dấu nháy cho chuỗi, đồ dài tối đa mỗi dòng …

    Đoạn code trên chỉ có vài dòng, nên bạn không thấy vấn đề gì to lớn cả, nhưng khi bạn viết hàng ngàn dòng code, nếu không đề ra sẵn bố cục và các quy ước, code của bạn sẽ trở thành một nồi lẩu thập cẩm mà chẳng ai muốn đọc.

II. Những xử lí giống nhau thì nên được viết tương tự nhau

    Khi bạn giải quyết một vấn đề, có những xử lí logic bắt buộc bạn phải thực hiện theo một trình tự nhất định mới có kết quả đúng, trong khi có những xử lí bạn có thể thực hiện theo một trình tự bất kì miễn là đủ các bước là được.

    Lời khuyên ở đây là: Kể cả khi không có yêu cầu về mặt thứ tự, bạn cũng nên đảm bảo luôn sắp xếp thứ tự các xử lí theo một trình tự nhất quán. Chúng ta nên thực hiện điều này bởi vì, khi những người khác đọc code của bạn, có thể họ sẽ không hiểu rõ về nghiệp vụ xử lí, nếu các đoạn code xử lí với những trình tự khác nhau, điều đó có thể khiến họ bối rối.

     Hãy thử xem xét tình huống, đoạn code để lấy thông tin user sau đây:


details = request.POST.get('details');
location = request.POST.get('location');
phone = request.POST.get('phone');
url = request.POST.get('url');

Ở một file xử lí khác, bạn cũng cần lấy thông tin user, nhưng code bạn viết như sau:


details = request.POST.get('details');
url= request.POST.get('url');
phone = request.POST.get('phone');
location = request.POST.get('location');

    Hãy để ý tới thứ tự lấy các thông tin. Thực ra, cả 2 đoạn code đều chạy đúng yêu cầu, không có lỗi gì cả, thế nhưng cách làm này phát sinh 1 vấn đề: làm thế nào để đảm bảo ta đã lấy hết những thông tin cần thiết từ user? Nếu user có tới 20 thông tin, nếu bạn không sắp xếp chúng theo 1 trình tự nhất định, thì rất có thể bạn sẽ lấy ra dư hoặc thiếu một thuộc tính nào đó.

    Nếu bạn luôn sắp xếp mọi thứ theo một trình tự quy ước sẵn, bạn sẽ tiết kiệm được rất nhiều thời gian khi cần kiểm tra, việc thêm/xoá/sửa một thuộc tính khác cũng sẽ thuận tiện hơn nhiều. Hãy chú ý tới điều này nhé.

III. Tách hàm cho những xử lí lặp lại nhiều lần

    Tiêu chí này là 1 phần nhỏ trong việc refactor code. Câu chuyện về việc refactor code để tăng hiệu xuất và tối ưu tính năng là 1 vấn đề lớn, chúng ta sẽ bàn vào dịp khác. Ở đây, chúng ta quan tâm tới việc tách hàm sao cho code xử lí trông gọn gàng hơn và người dùng code cũng có thể tái sử dụng dễ dàng.

    Quay trở lại với ví dụ ở phần II, thay vì chúng ta phải viết lại các dòng code giống nhau để xử lí cho cùng 1 việc, chúng ta nên tách những xử lí như vậy thành những hàm riêng. Việc làm này vừa giúp ta tránh viết code lặp lại, tránh được những sai sót khi clone code, cũng giúp hạn chế phạm vi ảnh hưởng nếu sau này logic có cần sửa đổi.

Chúng ta nên viết lại code như sau:

function getUserInfoFromRequest(request) {
   $info = array();

   $info['details'] = request.POST.get('details');
   $info['location'] = request.POST.get('location');
   $info['phone'] = request.POST.get('phone');
   $info['url'] = request.POST.get('url');

   return $info;
}

//Sử dụng trong file 1
$user_info1 = getUserInfoFromRequest(request);

//Sử dụng ở file 2
$user_info2 = getUserInfoFromRequest(request);

    Với cách viết như vậy, chúng ta đã giúp cho việc code dễ dàng hơn bằng các hàm dùng chung. Với bất cứ thay đổi nào về mặt nghiệp vụ, chúng ta chỉ cần sửa ở 1 chỗ duy nhất là hàm “getUserInfoFromRequest()”, code của ta sẽ thống nhất hơn rất nhiều.

IV. Gom những đoạn code liên quan thành từng đoạn

    Chúng ta sẽ bàn tới 2 vấn đề ở phần này: cách khai báo biến, và cách phân nhóm source code. Đây là 2 vấn đề cơ bản nhưng cũng rất quan trọng khi bạn viết code.

    Về cách khai báo biến, hãy nhớ: khai báo biến gần nơi sử dụng nhất có thể. Minh hoạ bằng đoạn code sau:


// Cách không tốt
int accountIndex = 0;
int totalAccount = 0;

... //Các đoạn code không liên quan

//Code sử dụng biến đã khai báo
...

//Cách cải thiện: dùng ở đâu, khai báo ở đó
int accountIndex = 0;
int totalAccount = 0;
//Sử dụng liền sau khi khai báo
...

    Việc sử dụng liền sau khi khai báo giúp chúng ta dễ dàng theo dõi tình trạng của biến hơn, cũng như tránh được tình trạng “quên sử dụng”.

    Về việc gom các xử lí có liên quan thành từng đoạn, ta có thể hình dung việc này bằng đoạn code sau đây:

//Đoạn code không tốt
friends = user.friends()
friend_emails = set(f.email for f in friends)
contacts = import_contacts(user.email, email_password)
contact_emails = set(c.email for c in contacts)
non_friend_emails = contact_emails - friend_emails
suggested_friends = User.objects.select(email__in=non_friend_emails)
display['user'] = user
display['friends'] = friends
display['suggested_friends'] = suggested_friends

//Ta nên tách các xử lí khác nhau ra
//Lấy thông tin emails
friends = user.friends()
friend_emails = set(f.email for f in friends)

//Xử lí emails
contacts = import_contacts(user.email, email_password)
contact_emails = set(c.email for c in contacts)
non_friend_emails = contact_emails - friend_emails
suggested_friends = User.objects.select(email__in=non_friend_emails)

//Hiển thị danh sách
display['user'] = user
display['friends'] = friends
display['suggested_friends'] = suggested_friends

    Bạn có thể thấy, chỉ với những thay đổi nhỏ, code của chúng ta trông có vẻ ngon lành hơn rất nhiều.

Tóm lại

   Trong phát triển phần mềm, chúng ta không chỉ đơn thuần là viết ra các source code mới, chúng ta cũng dành phần lớn thời gian cho việc bảo trì và phát triển những source code có sẵn. Vì lí do đó, làm thế nào để code hiệu quả, gọn gàng, súc tích, và “trông thuận mắt” là những điều rất quan trọng.

    Với những lưu ý mình đã nêu ra trong bài này, hi vọng mọi người có thể làm cho source code của mình ngon lành hơn, từ đó nâng cao năng suất làm việc hơn.

     Mọi người có thể xem lại các bài viết khác cùng chủ đề ở >>ĐÂY<< nhé.

     Chúc vui & enjoy coding 🙂

Tham khảo:
Sách: Clean Code: A Handbook of Agile Software Craftsmanship
Sách: The art of readable code

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s