Giới thiệu cuốn “The Art of Readable Code” (4)

Cuốn sách này của Dustin Boswell và Trevor Foucher tập trung vào các kỹ thuật đơn giản và hữu hiệu để viết code tốt hơn. Bạn có thể áp dụng các kỹ thuật này bất cứ khi nào bạn viết code. Tác giả trình bày các ý tưởng chính sau:

i. Cần viết code dễ hiểu

ii. Cần viết code để người đọc tốn ít thời gian nhất để hiểu. Tác giả sử dụng các ví dụ dễ hiểu được viết trên nhiều ngôn ngữ để trình bày trong các chương các khía cạnh khác nhau để giúp bạn viết code dễ hiểu.

I. Đặt tên, chú thích và định dạng đơn giản - sử dụng cho tất cả các dòng code bạn viết

II. Giảm thiểu sự phức tạp và mập mờ trong cách dùng các vòng lặp, logic và các biến

III. Xem xét vấn đề ở mức độ hàm, như cấu trúc lại các khối code để thực hiện 1 task 1 lần

IV. Viết code để test hiệu quả, chính xác và dễ đọc.

IV. Viết code để test hiệu quả, chính xác và dễ đọc.

Ví dụ, chúng ta có đoạn code test sau:

void Test1() {
  vector docs;
  docs.resize(5);
  docs[0].url = "http://example.com";
  docs[0].score = -5.0;
  docs[1].url = "http://example.com";
  docs[1].score = 1;
  docs[2].url = "http://example.com";
  docs[2].score = 4;
  docs[3].url = "http://example.com";
  docs[3].score = -99998.7;
  docs[4].url = "http://example.com";
  docs[4].score = 3.0;
  SortAndFilterDocs(&docs);
  assert(docs.size() == 3);
  assert(docs[0].score == 4);
  assert(docs[1].score == 3.0);
  assert(docs[2].score == 1);
}

1. Viết test dễ đọc hơn

Chúng ta có thể viết hàm sau

void AddScoredDoc(vector& docs, double score) {
  ScoredDocument sd;
  sd.score = score;
  sd.url = "http://example.com";
  docs.push_back(sd);
}

Sử dụng hàm này để viết lại chương trình test

void Test1() {
  vector docs;
  AddScoredDoc(docs, -5.0);
  AddScoredDoc(docs, 1);
  AddScoredDoc(docs, 4);
  AddScoredDoc(docs, -99998.7);
  ...
}

2. Dùng lời giải thích test đơn giản nhất

Dùng lời giải thích code test này là chúng ta có một danh sách các văn bản có các điểm số là [-5, 1, 4, -99998.7, 3]. Sau hàm SortAndFilterDocs(), các văn bản còn lại cần phải có điểm theo thứ tự sau [4, 3, 1]. Vậy code test của chúng ta nên có dạng CheckScoresBeforeAfter("-5, 1, 4, -99998.7, 3", "4, 3, 1");

3. Thực hiện Custom “Minilanguages”

Để ý rằng CheckScoresBeforeAfter() lấy 2 biến string là mảng của các điểm. Trong C++, viết dưới dạng array như sau CheckScoresBeforeAfter({-5, 1, 4, -99998.7, 3}, {4, 3, 1});

Vì chúng ta để các scores dưới dạng string, phân tách bởi dấm phảy nên chúng ta cần hàm phân tích các biến string và chuyển đổi giữa string và vector

void CheckScoresBeforeAfter(string input, string expected_output) {
  vector docs = ScoredDocsFromString(input);
  SortAndFilterDocs(&docs);
  string output = ScoredDocsToString(docs);
  assert(output == expected_output);
}
vector:
vector ScoredDocsFromString(string scores) {
  vector docs;
  replace(scores.begin(), scores.end(), ',', ' ');

  // Populate 'docs' from a string of space-separated scores.
  istringstream stream(scores);
  double score;

  while (stream >> score) {
    AddScoredDoc(docs, score);
  }
  return docs;
  }

  string ScoredDocsToString(vector docs) {
    ostringstream stream;
    for (int i = 0; i  0) stream << ", ";
    stream << docs[i].score;
  }
  return stream.str();
}

Ban đầu có vẻ như là cần nhiều code nhưng sau đó nó sẽ thực sự hữu dụng.

4. Thực hiện các thông báo lỗi dễ hiểu

Nếu chúng ta dùng

assert(output == expected_output)

Thông báo lỗi fail sẽ có dạng như sau

Assertion failed: (output == expected_output),
function CheckScoresBeforeAfter, file test.cc, line 37.

Ta sẽ không biết input và output nên ta có thể viết cách khác như sau sử dụng thư viện Boost C++

BOOST_REQUIRE_EQUAL(output, expected_output)

Bây giờ, nếu test fails, nó sẽ báo

test.cc(37): fatal error in "CheckScoresBeforeAfter": critical check
output == expected_output failed ["1, 3, 4" != "4, 3, 1"]

5. Chọn đúng test input

Thay vì chọn

CheckScoresBeforeAfter("-5, 1, 4, -99998.7, 3", "4, 3, 1");

ta có thể chọn

CheckScoresBeforeAfter("1, 2, -1, 3", "3, 2, 1");

Ta chọn các giá trị dương đơn giản để test việc sắp xếp và chọn 1 giá trị âm để test việc loại bỏ các giá trị âm đi.

6. Nhiều hàm test chức năng

Ví dụ 4 hàm sau

CheckScoresBeforeAfter("2, 1, 3", "3, 2, 1"); // Basic sorting
CheckScoresBeforeAfter("0, -0.1, -10", "0"); // All values < 0 removed
CheckScoresBeforeAfter("1, -2, 1, -2", "1, 1"); // Duplicates not a problem
CheckScoresBeforeAfter("", ""); // Empty input OK

7. Đặt tên hàm test

Thay vì đặt tên là

void Test1(){
...
}

ta có thể đặt là

void Test_SortAndFilterDocs(){
...
}

End