+2

Làm thế nào để có câu lệnh điều kiện, vòng lặp hoặc bất kỳ câu lệnh điều khiển nào khác dễ đọc hiểu hơn?

Trong một chương trình mà không có câu lệnh điều kiện, vòng lặp hoặc bất kỳ câu lệnh điều khiển nào khác thì sẽ rất dễ đọc hiểu. Tuy nhiên, trong thực tế, ta sẽ luôn luôn phải sử dụng những cấu câu lệnh điều kiện, vòng lặp .... Và tất nhiên ở đây, chúng ta sẽ làm cho những phần code sử dụng những thứ đó dễ đọc hiểu hơn. Chìa khóa ý tưởng xuyên suốt:

Hãy code tất cả các điều kiện, vòng lặp hoặc bất kỳ câu lệnh điều khiển nào khác... theo một cách tự nhiên nhất.

1. Thứ tự các đối số trong câu lệnh điều kiện Dưới đây là 2 ví dụ cùng mục đích là so sánh: Ví dụ 1:

if (length >= 10)

or

if (10 <= length)

Ví dụ 2:

while (bytes_received < bytes_expected)

or

while (bytes_expected > bytes_received)

Qua ví dụ 1, phần lớn lập trình viên đều có thể thấy case đầu tiên dễ đọc hơn rất nhiều. Nhưng qua ví dụ 2 thì sao? Case đầu tiên lại dễ đọc hơn. Vì sao lại vậy? Liệu có quy tắc chung gì ở đây không? Câu trả lời là có!

Đối số bên trái Đối số bên phải
Thường sẽ là biến số, giá trị mình cần đem ra so sánh Thường sẽ là một hằng số hoặc một giá trị nào đó để làm thước đo cho việc so sánh

Đến đây, sẽ có có người tự hỏi. Dễ đọc, dễ hiểu ở đây là như thế nào? Thực ra, đó chỉ là việc bạn cảm thấy việc đó thuận theo tự nhiên. Giống như việc bạn hít vào rồi thở ra vậy. Nếu như trong ngôn ngữ giao tiếp bình thường bạn sẽ thấy: Việc nói: "Nếu thu nhập bạn nhiều hơn $40 000/1 năm ..." sẽ cảm thấy tự nhiên hơn rất nhiều bạn nói: "Nếu $40000/1 năm là thu nhập nhiều hơn của bạn ..."

2. Thứ tự các khối if/else Thường khi viết các khối if/else, bạn sẽ không mấy để ý thử tự các khổi này. Bạn sẽ sắp xếp nó theo kiểu freestyle. Ví dụ như:

if (a == b) {
    // Case One ...
} else {
    // Case Two ...
}

hoặc:

if (a != b) {
    // Case Two ...
} else {
    // Case One ...
}

Vẫn theo nguyên tắc là hãy để mọi thứ tự nhiên! Ta sẽ có một vài nguyên tắc: * Hãy cố gắng sắp xếp những phép so sánh được coi là tích cực lên phía trên. Ví dụ như: if (debug) thay thế cho if (!debug) * Hãy cố gắng sắp xếp những case thực hiện đơn giản hơn lên phía trên. Tức là những case nào dễ giải quyết thì cho nó lên trên và xử lý trước. Còn những case phức tạp cho xuống sau. * Hãy cố gắng sắp xếp những case thứ vị hoặc dễ thấy hơn lên phía trên. Cái này cũng giống như bạn làm bài thi đại học thôi. Bài nào dễ thấy kết quả là làm trước.

Đó là 3 nguyên tắc. Tuy nhiên, khi áp dụng vào thực tế, chắc chắn sẽ có lúc bạn thấy có gì đó sai sai. Có gì đó đang đánh nhau. Dưới đây là ví dụ:

if not length:
    return
} elif not height:
    // Case Two

OK! OK! Bạn sẽ thấy ví dụ trên đúng với nguyên tắc thứ 2 và thứ 3 nhưng lại không đúng với nguyên tắc 1. Đúng trên 2/3 nguyên tắc. Không tồi. Vậy, vấn đề nữa ở đây, là bạn phải sử dụng linh hoạt 3 nguyên tắc thật làm sao để thật là hợp lý.

3. Phép toán điều kiện 3 ngôi Trong typescript: cond ? a : b Trong python: a if cond else b

Việc sử dụng phép toán điều kiện 3 ngôi này sẽ làm một đoạn code từ n dòng xuống còn 1 dòng! Nghe có vẻ nguy hiểm nhỉ !!! Ví dụ:

if (hour >= 12) {
    time_str += "pm";
} else {
    time_str += "am";
}

sau khi viết theo phép toán điều kiện 3 ngôi:

time_str += (hour >= 12) ? "pm" : "am";

Nhìn thấy nó ngắn gọn thật. Quan trọng hơn, nếu bạn là người đọc code, dòng code này cũng không phải là quá khó hiểu. Thêm một ví dụ nữa:

return exponent >= 0 ? mantissa * (1 << exponent) : mantissa / (1 << -exponent); 

Để coi nào! Sau 3s ... Sau 1 min ... Sau 5 min ... Sau 10 min OK! Đã hiểu! Có vấn đề rồi. Việc đọc hiểu đoạn code này mất quá nhiều thời gian. Vậy có nên? Không không, giữa việc đánh đổi việc rút ngắn một đoạn code mà mất nhiều thời gian hơn để đọc hiểu code là không nên.

Vậy nguyên tắc ở đây, đơn giản là bạn hãy sử dụng cấu trúc điều kiện 3 ngôi này trong trường hợp code này đơn giản nhất.

4. Return sớm nhất có thể từ function Một vài coder tin rằng mỗi function ko nên có nhiều return. Tuy nhiên, return về sớm hoàn toàn tốt và thường được mong muốn. Ví dụ:

public boolean Contains(String str, String substr) {
    if (str == null || substr == null) return false;
    if (substr.equals("")) return true;

5. Các vấn đề liên quan tới Nesting Một chương trình có cấu trúc lồng nhau:

if ...
    if ...
        if ...
            if ...
                do something
            endif
        endif
    endif
endif

Kiểu cấu trúc này có một vẫn đề là rất khó để đọc hiểu vì có quá nhiều khối if/else, do..while ... lồng nhau. Vậy giải pháp ở đây là gì?

  • Kết hợp các câu lệnh if/else:
if (a && b) {
    // Case ...
}

tốt hơn:

if (a) {
    if (b) {
        // Case ...
    }
}
  • Return sớm:
if not a:
    return ...
if not b:
    return ...

tốt hơn:

if a:
    ...
if b:
    ...

Ví dụ khác:

for i in l:
    if not a:
        continue
    if not b:
        continue

tốt hơn:

for i in l:
    if a:
        ….
    if b:
        ….
  • Else luôn return
if (a) {
    return … 
} 
return … 

tốt hơn:

if (a) {
    return...
} else {
    return...
}
  • Sử dụng switch ... case
swith (x) {
    case a:
        return …
    case b:
        return …
    default:
        return ...
    }
  • Sử dụng elseif nếu ngôn ngữ đó hỗ trợ
if a:
    return ...
elif b:
    return ...
return …

tốt hơn

if a:
    return ...
else:
    if b:
        return ...
    else:
        return ...

6. Mở rộng một chút Nested Nested loops là vòng lặp đa chiều tự nhiên. Nhưng để xử lý tuần tự các dữ liệu một chiều, các vòng lồng nhau thường không tự nhiên và có thể được thay thế bởi cấu trúc phẳng hơn. Ví dụ cụ thể:

int i = 0;
for (; i < n; i++) {
    foo(a[i]);
    if (...)
        break;
}
for (; i < n; i++) {
    bar(a[i])
}

tốt hơn:

for (int i = 0; i < n; i++) {
    foo(a[i]);
    if (...) {
        for (int j = i; j < n; j++) {
            bar(a[j])
        }
    }
}

Cảm ơn vì đã đọc !.


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí