clean code tiếng việt chap 6

Bản clean code tiếng việt chap 6. Mong mọi người để lại những góp ý về bản dịch này.

Chúng tôi có lý do khi muốn giữ các biến là private. Chúng tôi không muốn ai đó phụ thuộc vào chúng. Chúng tôi muốn giữ để thay đổi kiểu dữ liệu hoặc thực hiện các hành động tùy biến. Nhưng sau đó thì sao?  Đã có rất nhiều lập trình viên tự động thêm getter và setter vào class của họ, điều đó chẳng khác gì thay đổi các biến private thành public.

1. Trừu tượng hóa dữ liệu

Xem xét sự khác biệt giữa Listing 6-1 và Listing 6-2. Cả hai đều đại diện cho dữ liệu của một điểm trong hệ tọa độ. Tuy nhiên, một cái thì để lộ việc triển khai trong khi cái còn lại thì không.

Trừu tượng hóa dữ liệu
Trừu tượng hóa dữ liệu

Cái hay của Listing 6-2 là không có cách nào bạn biết được việc triển khai interface. Interface rõ ràng là một dạng cấu trúc dữ liệu.

Các phương thức tuân theo một chính sách duy nhất. Bạn có thể đọc các tọa độ riêng lẻ một cách độc lập, nhưng bạn phải đặt các tọa độ lại với nhau để tạo nên một cấu trúc đầy đủ.

Listing 6-1, mặt khác, thể hiện rất rõ ràng. Điều này làm lộ dữ liệu ngay cả khi các biến là private và chúng tôi đang sử dụng các getter/setter.

Ẩn việc triển khai không đơn giản là việc đặt một biến vào giữa các hàm, đó là vấn đề trừu tượng hóa. Một class không chỉ đơn giản là đẩy các biến của nó thông qua các getter/setter. Thay vào đó nó cung cấp các abstract interface cho phép người dùng thao tác với bản chất của dữ liệu mà không cần quan tâm đến cách mà chúng hoạt động.

Chúng tôi không muốn tiết lộ chi tiết về dữ liệu, thay vào đó chúng tôi muốn giữ dữ liệu của chúng tôi ở các dạng trừu tượng. Điều này không chỉ đơn thuần được thực hiện bằng cách sử dụng các interface và/hoặc getter và setter. Sử dụng getter/setter là tùy chọn tồi tệ nhất mà tôi sẽ thực hiện.

2. Cấu trúc dữ liệu và đối tượng

Hai ví dụ bên dưới cho thấy sự khác biệt giữa các dữ liệu có cấu trúc và đối tượng.

Các đối tượng ẩn dữ liệu của chúng bên dưới trừu tượng hóa và đưa ra các hàm dựa trên những dữ liệu đó.

Dữ liệu có cấu trúc phơi bày dữ liệu của chúng và không có các hàm có nhiều ý nghĩa.

Lưu ý bản chất của hai định nghĩa này. Chúng thực sự đối lập nhau. Sự khác biệt này có vẻ không đáng kể, nhưng nó có ảnh hưởng sâu đến hệ thống.

Ví dụ, xem xét ví dụ về các class liên quan đến hình học trong Listing 6-5:

Class Geometry hoạt động với dữ liệu của ba class khác, là các cấu trúc dữ liệu đơn giản mà không có bất kỳ phương thức nào. Các hành động đều thuộc về class Geometry.

cấu trúc dữ liệu đơn giản
cấu trúc dữ liệu đơn giản

Các lập trình viên hướng đối tượng có thể sẽ không đồng ý với điều này và cho rằng đó là một phương pháp của họ  và chúng đúng. Nhưng người khác có thể nhìn vào và chê cười vì chúng không được quan tâm và bảo trì.

Chuyện gì xảy ra nếu tôi thêm vào class Geometry một hàm nữa? Không sao cả, các class bên trên nó sẽ không bị ảnh hưởng. Nhưng nếu tôi thêm vào một class mới, tôi phải thay đổi các hàm trong class Geometry để phù hợp với nó.

Một lần nữa, chúng ta thấy sự đối lập giữa hai vấn đề này. Điều này chỉ ra sự khác biệt cơ bản giữa các đối tượng và cấu trúc dữ liệu:

Các dòng code sử dụng phương pháp cấu trúc dữ liệu giúp dễ dàng thêm vào các hàm mới mà không cần phải thay đổi cấu trúc của dữ liệu hiện tại.

Mặt khác, code theo phương pháp hướng đối tượng giúp dễ dàng thêm các class mới mà không thay đổi các hàm đã viết.

Bạn cũng có thể hiểu nó như sau:

Code theo cấu trúc dữ liệu làm bạn khó thêm dữ liệu mới vì phải thay đổi toàn bộ hàm. Code theo hướng đối tượng làm bạn khó thêm hàm vì phải thay đổi tất cả các class chịu ảnh hưởng.

Vậy là, ưu điểm của phương pháp này lại là nhược điểm của phương pháp kia, và ngược lại.

Trong bất kỳ hệ thống nào, có lúc chúng ta muốn bổ sung các kiểu dữ liệu mới thay vì các hàm mới. Trong trường hợp này phương pháp hướng đối tượng sẽ phù hợp hơn.

Nhưng cũng có lúc chúng ta muốn thêm hàm mới thay vì thêm dữ liệu. Trong trường hợp này, cấu trúc dữ liệu nên được ưu tiên hơn.

Các lập trình viên giàu kinh nghiệm biết rằng ý tưởng đối tượng hóa mọi thứ là chuyện hoang đường.

3. The Law of Demeter

Kỹ thuật Law of Demeter nói rằng một module không nên biết về thành phần bên trong của một đối tượng mà nó sử dụng.

Điều này có nghĩa là các đối tượng không nên phơi bày cấu trúc của chúng thông qua các getter/setter . Vì việc này làm lộ cấu trúc bên trong nó, điều chúng ta cần làm là ẩn chúng đi.

3.1 Train Wrecks

Dạng code này thường được gọi là train wreck  vì nó trông giống như một loạt các toa tàu được ghép lại với nhau.

Vấn đề này sẽ ít gây nhầm lẫn hơn nếu các cấu trúc đơn giản và chỉ có biến public và không có phương thức, trong khi các đối tượng có các biến private và các hàm public. Tuy nhiên, có một vài framework yêu cầu rằng ngay cả các cấu trúc dữ liệu đơn giản cũng cần phải có các hàm truy cập.

3.2 Con lai

Sự nhầm lẫn này đôi khi tạo nên các đứa con lai, mang nửa dòng máu đối tượng và một nửa còn lại là cấu trúc.

Chúng có các hàm làm những việc quan trọng, chúng cũng có các biến public hoặc các hàm truy cập public,… Với mục đích cuối cùng là hô biến các biến private thành public. Làm các hàm bên ngoài sử dụng các biến private (thông qua getter/setter) như một cấu trúc đơn giản.

Những đứa con lai này khiến cho việc thêm mới hàm trở nên khó khăn, và việc thêm thuộc tính mới cũng khó khăn nốt. Chúng là thứ tồi tệ nhất mà bạn sẽ tạo ra, vậy nên đừng tạo ra chúng.

4. Data Transfer Objects

Dạng thuần túy của cấu trúc dữ liệu là class có các biến public và không có hàm. Dạng này được gọi là một đối tượng truyền dữ liệu (Data transfer object), hoặc DTO. DTO là cấu trúc rất hữu ích, đặc biệt là khi giao tiếp với cơ sở dữ liệu hoặc chuyển đổi thông điệp từ các socket, v.v.

thuần túy của cấu trúc dữ liệu
thuần túy của cấu trúc dữ liệu clean code tiếng việt

4.1 Active Records

Active Record là các hình thức DTO đặc biệt. Chúng là các cấu trúc dữ liệu với các biến public, đôi khi có các phương thức như Save và Find. Thông thường các Active Record là dữ liệu được gửi trực tiếp từ các bảng trong cơ sở dữ liệu hoặc các nguồn dữ liệu khác.

chúng ta thường thấy các nhà phát triển đối xử với cấu trúc này như thể chúng là đối tượng bằng cách đặt các phương thức nghiệp vụ vào chúng. Điều này thật nguy hiểm.

5. Kết luận

Đối tượng hiển thị ra các hành động và ẩn dữ liệu. điều này giúp dễ dàng thêm các loại đối tượng mới mà không thay đổi các hành vi hiện có. Nhưng nó cũng làm cho việc thêm các phương thức mới vào đối tượng hiện có trở nên khó khăn.

Trong bất kỳ hệ thống nào, đôi khi chúng tôi sẽ muốn việc thay đổi dữ liệu trở nên linh hoạt. Vì vậy chúng tôi chọn đối tượng cho hệ thống. Nhưng thỉnh thoảng cần thêm những hàm mới, và vì vậy chúng tôi cần chọn kiểu cấu trúc. Các nhà phát triển phần mềm giỏi luôn biết cách tiếp cận tốt nhất trong những trường hợp này.

6. Tham khảo

clean code tiếng việt chap 1

Cảm ơn các bạn đã đọc. Mong nhận được những góp ý của các bạn để mình hoàn thiện hơn nữa

clean code tiếng việt chap 4

Đừng comment trên Code xấu – Hãy viết lại nó.”

Không gì hữu ích hơn bằng một comment được đặt đúng chỗ. Nhưng cũng sẽ trở nên tồi tệ, gây nhiễu loạn thông tin khi đặt sai hay là cung cấp thông tin không đúng.

Việc dùng đúng những comment là một cách để bù đắp cho sự thất bại của chúng ta trong việc thể hiện ý nghĩa của những dòng code. Chính xác là vậy – comment luôn luôn là sự thất bại.

Vậy nên, khi bạn cần viết comment, bạn hãy suy nghĩ kỹ xem liệu có cách nào đó để biến các dòng code thành chính xác những gì bạn muốn thể hiện hay không.

Điều tin tưởng tuyệt đối nhất là: Code. Chỉ có code mới nói cho bạn biết thật sự là nó làm gì. Nó là nguồn thông tin chính xác duy nhất. Do đó, mặc dù comment đôi khi là cần thiết, nhưng chúng tôi sẽ cố gắng để giảm thiểu nó.

Đừng dùng comments để trang trí cho mã xấu (Comments Do Not Make Up for Bad Code)

Một trong những động lực to lớn để viết comment là do code tồi, code tối nghĩa.

Khi chúng ta viết một module và chúng ta biết rằng nó lộn xộn và vô tổ chức. Chúng ta biết nó bữa bộn Vậy nên chúng ta tự nhủ rằng: “Ồ, tốt hơn nên viết comment ở đây!”. Không! Tốt hơn bạn nên viết lại code!

Code rõ ý nghĩa và rõ ràng với ít comment sẽ tuyệt vời hơn so với code tối nghĩa, phức tạp với nhiều comment. Thay vì dành thời gian để viết bình luận cho mớ lộn xộn, bạn nên dọn dẹp nó.

Giải thích ý nghĩa ngay trong code(Explain Yourself in Code)

Trong nhiều trường hợp, việc thay thế code nghèo nàn bằng cách tạo ra những hàm mới đã nói lên đủ những lời comment mà bạn muốn viết dành riêng cho đoạn code đó.

// Check to see if the employee is eligible for full benefits

if ((employee.flags & HOURLY_FLAG) &&

(employee.age > 65))

Hay cái này?

if (employee.isEligibleForFullBenefits())

comment code
comment code

Good Comments

Một số comment là cần thiết hoặc có ích. Chúng ta sẽ xem một số trường hợp được cho là xứng đáng để bạn bỏ công ra viết.

  • Bình luận pháp lý (Legal Comments)

Điều này tùy thuộc vào tiêu chuẩn viết code của dự án bạn đang làm. Ví dụ: Bản quyền và quyền tác giả là điều cần thiết và hợp lý để đưa vào comment lúc bắt đầu của mỗi tập tin mã nguồn.

/* Copyright (C) 2003,2004,2005 by Object Mentor, Inc.
* All rights reserved.

* Released under the terms of the GNU General Public License version
* 2 or later.
*/

  • Bình luận cung cấp thông tin (Informative Comments)

Cung cấp thông tin với vài dòng comment đôi khi khá là hữu dụng.

// Returns an instance of the Responder being tested.

protected abstract Responder responderInstance();

Một comment như vậy đôi khi khá là hữu ích, nhưng tốt hơn hết là nên truyền tải thông tin thông qua tên của function nếu có thể. (responderBeingTested.)

// format matched kk:mm:ss EEE, MMM dd, yyyy

Pattern timeMatcher = Pattern.compile(

“\\d*:\\d*:\\d* \\w*, \\w* \\d*, \\d*”);

Trong trường hợp này comment cho chúng ta biết định dạng về thời gian trong hàm SimpleDateFormat.format với một chuỗi xác định. Tuy nhiên, nó vẫn có thể rõ ràng hơn nếu mã này được chuyển sang một lớp đặc biệt với mục đích chuyển đổi định dạng thời gian. Sau đó, những comment có thể sẽ không cần thiết nữa.

  • Giải thích về mục đích (Explanation of Intent)

Đôi khi comment không chỉ cung cấp thông tin về những dòng code mà còn cung cấp ý định đằng sau nó.

public int compareTo(Object o)

{

if(o instanceof WikiPagePath)

{

WikiPagePath p = (WikiPagePath) o;

String compressedName = StringUtil.join(names, “”);

String compressedArgumentName = StringUtil.join(p.names, “”);

return compressedName.compareTo(compressedArgumentName);

}

return 1; // we are greater because we are the right type.

}

  • Làm dễ hiểu (Clarification)

Đôi khi bạn cần dùng comment để diễn giải ý nghĩa của các đối số khó hiểu hoặc giá trị trả về, để biến chúng thành thứ gì đó có thể hiểu được.

Nhưng chỉ khi một phần thư viện chuẩn hoặc trong code mà bạn không thể thay đổi được, một bình luận có thể sẽ hữu ích.

{

   WikiPagePath a = PathParser.parse(“PageA”);

   WikiPagePath ab = PathParser.parse(“PageA.PageB”);

   WikiPagePath b = PathParser.parse(“PageB”);

   WikiPagePath aa = PathParser.parse(“PageA.PageA”);

   WikiPagePath bb = PathParser.parse(“PageB.PageB”);

   WikiPagePath ba = PathParser.parse(“PageB.PageA”);

   assertTrue(a.compareTo(a) == 0); // a == a

   assertTrue(a.compareTo(b) != 0); // a != b

   assertTrue(ab.compareTo(ab) == 0); // ab == ab

   assertTrue(a.compareTo(b) == -1); // a < b

   assertTrue(aa.compareTo(ab) == -1); // aa < ab

   assertTrue(ba.compareTo(bb) == -1); // ba < bb

   assertTrue(b.compareTo(a) == 1); // b > a

   assertTrue(ab.compareTo(aa) == 1); // ab > aa

   assertTrue(bb.compareTo(ba) == 1); // bb > ba

}

Dĩ nhiên, khả năng các comment dạng này cung cấp thông tin không chính xác là khá cao. Làm dễ hiểu (Clarification) là cần thiết và nó cũng đầy rủi ro. Vì vậy, trước khi viết bình luận như vậy hãy quan tâm xem có cách nào tốt hơn không, để có một lựa chọn đúng đắn nhất.

  • Cảnh báo về các hậu quả (Warning of Consequences)

Đôi khi nó rất hữu ích để cảnh báo các lập trình viên khác về hậu quả xảy ra. Ví dụ, giải thích lý do tại sao một test case cụ thể bị tắt đi:

// Don’t run unless you

// have some time to kill.

public void _testWithReallyBigFile()

{

   writeLinesToFile(10000000);

   response.setBody(testFile);

   response.readyToSend(this);

   String responseString = output.toString();

   assertSubString(“Content-Length: 1000000000”, responseString);

   assertTrue(bytesSent > 1000000000);

}

Có thể còn nhiều cách tốt hơn để giải quyết vấn đề này. Tôi đồng ý. Nhưng bình luận được đưa ra ở đây là hoàn toàn hợp lý. Nó ngăn chặn một số lập trình viên khác.

  • TODO Comments

TODO là những công việc mà lập trình viên nghĩ là cần nên làm, nhưng vì lý do nào đó mà họ không thể thực hiện nó ngay lúc này. Nó có thể là một lời nhắc nhở để xóa một tính năng không cần thiết, một lời bào chữa cho ai đó khi nhìn vào vấn đề. Nó có thể là một yêu cầu dành cho ai đó để nghĩ ra một cái tên hay hơn, hay nhắc nhở về một sự thay đổi nào đó cho sự kiện đã được lên kế hoạch từ trước. Nhưng dù TODO có là gì đi nữa, nó chắc chắn không phải là lý do để bạn quăng đống code ẩu, code bừa vào dự án.

  • Amplification (Phóng đại)

Comment có thể được dùng để khuếch đại tầm quan trọng của một cái gì đó có vẻ không quan trọng:

String listItemContent = match.group(3).trim();

// the trim is real important. It removes the starting

// spaces that could cause the item to be recognized

// as another list.

new ListItemWidget(this, listItemContent, this.level + 1);

return buildList(text.substring(match.end()));

  • Javadocs in Public APIs

Không có gì hữu ích và tuyệt vời bằng public API được mô tả tốt. Các javadoc của thư viện chuẩn của Java là một trường hợp điển hình. Sẽ rất khó để viết các chương trình Java mà thiếu chúng.

Bad Comments

Đa số các comment rơi vào thể loại này. Chúng thường được sử dụng như cái cớ cho việc viết code rởm hoặc biện minh cho các cách giải quyết đầy thiếu sót.

  • Độc thoại

Quăng vào một comment chỉ vì bạn thấy thích.

  • Các comment thừa thải

Những comment này, có lẽ, còn làm người đọc mất thời gian hơn so với việc đọc code của hàm.

  • Các comment sai sự thật

….Update …

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 đó.

sách clean code chap 1

clean code tiếng việt
clean code tiếng việt

Trước hết, bạn đọc cuốn sách này bởi hai lý do:

Thứ nhất, bạn đang là một lập trình viên. Thứ hai, bạn muốn

trở thành một lập trình viên thật giỏi. Tuyệt vời! Chúng tôi cần lập trình viên giỏi.

Clean code là một quyển sách nói về những cách để bạn code tốt hơn. Chúng ta sẽ xem xét code trên nhiều phương diện, từ trên xuống dưới, từ dưới lên trên, và  từ trong ra ngoài. Khi xong việc, chúng ta sẽ được biết thêm rất nhiều về code. Từ đó, chúng ta sẽ nói về sự khác biệt giữa good code và bad code.

There Will Be Code(Sẽ vẫn có mã )

Nhiều người cho rằng việc viết code sau vài năm nữa sẽ không còn là vấn đề. Rằng chúng ta cần quan tâm đến những mô hình và các yêu cầu là đủ. Thực tế một số người đang cho rằng việc viết code dần đến lúc phải kết thúc, code sẽ được tạo ra thay vì được viết hay gõ. Và rồi, lập trình viên sẽ không cần thiết nữa bởi những khách hàng kinh doanh sẽ tự tạo ra các chương trình bằng cách nhập các thông số kỹ thuật.

Oh. Điều này là vô lý! Code sẽ không bao giờ bị loại bỏ bởi vì code đại diện cho chi tiết, nội dung của các yêu cầu từ khách hàng. Ở một mức độ nào đó, những chi tiết, nội dung đó không thể mang tính trừu tượng hóa mà cần sự rõ ràng. Và việc thiết lập các nội dung mà máy tính có thể hiểu và thi hành chính là việc lập trình(viết code).

Hy vọng rằng mức độ trừu tượng của các ngôn ngữ lập trình sẽ tiếp tục tăng, có thêm nhiều ngôn ngữ hơn nữa. Đó là một dấu hiệu rất tốt,Nhưng nó không thể loại bỏ việc viết code. Dù có nhiều những công cụ hỗ trợ nhưng việc viết code nó vẫn phải được thực hiện một cách nghiêm túc, chính xác để máy tính có thể hiểu được và thực hiện.

Có nhiều người nghĩ là việc viết code một ngày nào đó sẽ biến mất. Khi mà máy tính có thể hiểu được những yêu cầu mơ hồ và thực hiện nó một cách chính xác. Họ mơ rằng sẽ có một ngày chúng ta sẽ tạo ra được một cỗ máy có thể làm được những gì chúng ta muốn. Dĩ nhiên, chuyện đó chỉ xãy ra trong phim viễn tưởng.

"<yoastmark

Điều đó là phi thực tế!

Hãy nhớ một điều rằng code là một ngôn ngữ mà trong đó, công việc cuối cùng của chúng ta là thể hiện những yêu cầu.

Nhưng chúng ta sẽ không bao giờ loại bỏ code – so there will always be code!

Bad Code(Code rởm)

Tác giả nói là có đọc phần mở đầu của quyển Implementation Patterns.1 của Kent Beck. Ông ấy nói rằng “…cuốn sách này dựa trên một tiền đề khá mong manh: đó là vấn đề code sạch…” và tác giả không đồng ý với nó.Tác giả cho rằng good code chính là tiền đề lớn nhất trong lĩnh vực lập trình. Đội ngũ của tác giả đã phải mặt với vấn đề ‘good code’ quá lâu.

Tiếp theo là một ví dụ về một ứng dụng, nó rất phổ biến. Nhưng sau đó, những quá trình cập nhật sửa lỗi bắt đầu bị kéo dài ra, có nhiều lỗi thì không được sửa từ phiên bản này qua phiên bản khác, thời gian tải và sự cố cũng theo đó mà tăng lên nhiều hơn. Tác giả cũng đã ngưng sử dụng sản phẩm trong sự thất vọng và không dùng lại nó nữa.  Một thời gian sau, công ty đó cũng ngừng hoạt động.

Hai mươi năm sau, tác giả gặp một trong những nhân viên ban đầu của công ty đó và hỏi anh ta chuyện gì đã xãy ra. Câu trả lời đã khiến tác giả lo sợ : Họ đã đưa sản phẩm ra thị trường cùng với một đống code hỗn độn trong đó. Khi các tính năng mới được thêm vào ngày càng nhiều, code của ứng dụng càng ngày càng tệ, đến mức không thể kiểm soát được nữa. Tác giả gọi đó là những dòng code bad(rởm).

Bạn là lập trình viên và bạn đã bao giờ bị những dòng mã bẩn gây khó dễ chưa? Nếu bạn là một lập trình viên có kinh nghiệm, chắc hẳn bạn đã từng trải qua cảm giác đó một vài lần. Chính xác hơn là lội qua mã bẩn. Bơi trong một mớ lộn xộn với những cái bug được giấu kín.

Nếu bạn đã từng bị những dòng code “rởm” cản trở như tôi miêu tả, vậy thì – tại sao bạn lại tạo ra nó?

Có thể bạn cố gắng làm nhanh hơn? Bạn đã vội vàng? Hoặc bạn cảm thấy bạn không có đủ thời gian để hoàn thành nó. Rằng sếp sẽ tức giận nếu bỏ thời gian làm sạch mà không làm cho xong kịp dealine. Cũng có lẽ bạn đã quá mệt mỏi với cái chương trình chết tiệt này và muốn kết thúc nó ngay. Và tự nhủ rằng sẽ quay lại và dọn dẹp nó ngay sau đó.

 

Cái giá của sự lộn xộn(The Total Cost of Owning a Mess)

Code bẩn sẽ làm cho công việc chậm lại một cách đáng kể.

Cái giá của sự lộn xộn(The Total Cost of Owning a Mess)

Code bẩn sẽ làm cho công việc chậm lại một cách đáng kể.

Năng suất làm việc theo đó giảm dần và tiệm cận về 0.

Khi hiệu suất giảm, người quản lý cần làm công việc của họ – thêm nhiều thành viên mới với hy vọng cải thiện tình trạng. Nhưng những nhân viên mới thường sẽ không nắm rõ cách hoạt động hoặc thiết kế của hệ thống. Vậy là, càng làm việc, họ càng tạo ra nhiều code rối, và đưa cả nhóm (một lần nữa) dần tiến về 0.

Đập đi để xây lại

Cuối cùng, cả nhóm quyết định cần phải thiết kế lại.Dĩ nhiên người quản lý không muốn mất thêm tài nguyên cho việc tái khởi động dự án, nhưng họ cũng không thể phủ nhận sự thật rằng hiệu suất làm việc của cả nhóm đang rất thấp. Cuối cùng, họ chấp nhận theo yêu cầu của các lập trình viên và cho phép bắt đầu lại dự án.

Một team hổ mới được lựa chọn. Mọi người đều muốn vào team này bởi vì đây là một dự án mới. Nhưng chỉ những người giỏi nhất và có triển vọng mới được lựa chọn. Những người còn lại sẽ tiếp tục bảo trì hệ thống hiện tại.

Bây giờ thì cả hai team đều đang ở trong một cuộc đua. Nhóm mới thì phải xây dựng một hệ thống mới với những chức năng của hệ thống cũ, và những thay đổi cho hệ thống cũ. Ban quản lý sẽ không thay đổi hệ thống cũ cho tới khi hệ thống mới hoàn thành và đáp ứng được yêu cầu.

Cuộc đua này có khi sẽ phải diễn ra trong một thời gian rất dài. Tôi đã từng thấy một cuộc đua như vậy, nó mất đến 10 năm để kết thúc. Và tại thời điểm đó, những thành viên ban đầu của nhóm mới đã nghỉ việc. Các thành viên hiện tại đang yêu cầu thiết kế lại hệ thống vì code của nó đã trở thành một mớ hỗn độn.

Việc dành thời gian để giữ cho code sạch đẹp không chỉ là câu chuyện về chi phí, mà đó còn là vấn đề sống còn của lập trình viên.

Thái độ (Attitude)

Bạn đã bao giờ bơi qua một đống code lộn tùng phèo trong vài tuần trong khi chỉ cần một vài giờ để xử lý nó? Những gì bạn thấy là thay đổi một dòng thay vì thực hiện ở hàng trăm module khác nhau. Nếu thật vậy, bạn không hề cô đơn, ngoài kia có hàng trăm ngàn lập trình viên như bạn.

Tại sao chuyện này lại xảy ra? Tại sao từ Good Code nhanh chóng bị mục nát trở thành Bad Code? Có rất nhiều lý do, các yêu cầu thay đổi theo hướng ngăn cản thiết kế ban đầu, lịch làm việc quá chặt chẽ và khách hàng không khoan nhượng,… Nhưng lỗi ở trong chính chúng ta.

Một lập trình viên không chuyên nghiệp, bạn sẽ đổ lỗi cho những yêu cầu thay đổi chóng mặt của khách hàng, deadline của manager,..

Để giải thích điều này, hãy tưởng tượng bạn là bác sĩ và có một bệnh nhân yêu cầu bạn hãy ngưng việc rửa tay để chuẩn bị cho phẫu thuật, bởi việc rửa tay mất quá nhiều thời gian. Rõ ràng bệnh nhân là thượng đế, nhưng bác sĩ sẽ luôn từ chối yêu cầu này. Vì sao? Vì bác sĩ biết nhiều hơn bệnh nhân về những nguy cơ về bệnh tật và nhiễm trùng. Thật là ngu ngốc khi bác sĩ lại đồng ý với những yêu cầu như vậy.

Vậy nên là một lập trình viên chuyên nghiệp bạn cần bảo vệ Code của mình (mặc dù chậm Deadline nhưng vẫn phải sạch) trước ông manager. Vì ông manager cũng như tay bệnh nhân kia, chả biết quái gì về Code sạch và cái giá phải trả cho hàng loạt Code bẩn.

Vấn đề nan giải (The Primal Conundrum)

Cách duy nhất để hoàn thành đúng hạn – cách duy nhất để bước đi vững vàng – là giữ cho code luôn sạch sẽ nhất khi bạn còn có thể.

Nghệ thuật viết mã sạch (The Art of Clean Code?)

Làm thế nào để viết Code sạch?

Viết Code sạch là một nghệ thuật, đơn giản bạn có thể nhìn vào và nhận biết đâu là Code sạch, đâu là Code bẩn. Nếu bạn không biết ý nghĩa của việc code sạch, tốt nhất bạn không nên viết nó.

Đề viết code sạch sẽ yêu cầu sự khổ luyện liên tục những kỹ thuật nhỏ khác nhau, và sự cần cù sẽ được đền đáp bằng “sạch sẽ” của code. Khi có được giác quan nhận biết được đâu là Code sạch đâu là Code bẩn, và có thể biến Code bẩn thành Code sạch, có được cảm giác và lựa chọn thay đổi tốt nhất.

Tóm lại, lập trình viên viết code “sạch đẹp” như là một nghệ sĩ. Họ có thể tạo ra các hệ thống thân thiện chỉ từ một màn hình trống rỗng.

Mã sạch là gì (What Is Clean Code?)

Có rất nhiều định nghĩa về code sạch. Vì vậy chúng tôi phóng vấn một số lập trình viên nổi tiếng và nhiều kinh nhiệm về lĩnh vực này.

Bjarne Stroustrup (Cha đẻ C++) và là tác giả của quyển The C++ Programming Language:

“Tôi thích code của tôi trông thanh lịch và hiệu quả. Sự logic nên được thể hiện rõ ràng để làm cho các lỗi khó lẫn trốn, sự phụ thuộc được giảm thiểu để dễ bảo trì, các lỗi được xử lý bằng các chiến lược rõ ràng, và hiệu năng thì gần như tối ưu để không lôi kéo người khác tạo nên code rối bằng những cách tối ưu hóa tạm bợ. Code sạch sẽ tạo nên những điều tuyệt vời”.

Grady Booch, tác giả quyển Object Oriented Analysis and Design with Applications

“Đơn giản và rõ ràng. Code sạch được đọc như đọc văn xuôi được viết trôi chảy. Không làm lu mờ đi mục đích thiết kế, nhưng vẫn đầy đủ các khái niệm trừu tượng hóa rõ ràng và những dòng mã đơn giản dễ hiểu.”

Michael Feathers , tác giả quyển Working Effectively with Legacy Code:

Tôi có thể liệt kê tất cả những phẩm chất mà tôi thấy trong code sạch, nhưng tất cả chúng được bao quát bởi một điều – code sạch trông như được viết bởi những người tận tâm”…

Mình đã tóm tắt nội dung những ý chính của chương này. Mọi người có góp ý để lại comment bên dưới giúp mình nhé.