Trả về một danh sách rỗng và trả về giá trị null, cách nào tốt hơn?

Bài đầu tiên ngồi dịch mà lại chọn ngay bài khó nhằn lại còn dài thế này, mà lỡ rồi, nó đập vào mắt đầu tiên trên twitter sáng nay, hứng phải làm liền, xệp uổng lắm =)

Bài này được dịch từ nguồn: http://www.codeproject.com/Articles/794448/Is-it-Really-Better-to-Return-an-Empty-List-Instea

Phần 1: Giới thiệu

Phần 2: So sánh 2 cách tiếp cận

Phần I : Giới thiệu

Một lời khuyên phổ biến : Đừng trả về Null

Có một lời khuyên khá phổ biến và được chấp nhận rộng rãi trong giới phát triển phần mềm:

Nên luôn trả về một danh sách rỗng thay vì giá trị null!

Có 2 lợi ích lớn được đưa bởi những người đưa ra tranh luận về phát biểu trên:

  • Loại bỏ được độ rủi ro khi trả về giá trị null (i.e NullReferenceException trong C#, hay NullPointerException trong Java, ect.)
  • Không phải kiểm tra null, mã nguồn sẽ ngắn hơn, dễ đọc và sửa hơn sao này.

Vài người khăng khăng về phát biểu trên, ví dụ điển hình hãy nhìn thử đoạn trích trong một câu trả lời  nằm ở top đầu trên StackOverflow về câu hỏi : Is it better to return null or empty collection? Luôn luôn là tập hợp rỗng, đây:

Họ luôn cho rằng không bao giờ trả về null khi phải trả về một collection hay enumerable. LUÔN LUÔN trả về enumerable/collection rỗng nhằm tránh những lợi ích tào lao (:D) ở trên, và ..

Nhiều bình luận còn đưa ra nhiều lợi ích khác :

 … Làm vậy còn để tránh đồng nghiêp hay người sử dụng class không bôi trứng lên xe của bạn :))

Lời khuyên này còn được chống lưng bởi một số người khá nổi và lời nói có tầm ảnh hưởng lớn:

  • Book Clean Code; by Robert C. Martin; page 110: Đừng trả về giá trị null
  • Book Effective Java 2nd edition; by Joshua Bloch; page 201: Item 43: Trả về một tập hợp rỗng, không phải null
  • Book Framework Design Guidelines 2nd edition; by Krzysztof Cwalina and Brad Abrams; page 256: ĐỪNG trả về giá trị null từ những thuộc tính kiểu tập hợp hay hàm kiểu trả về là tập hợp. Hãy trả về một tập hợp hay mảng rỗng
  • Book Pattern of Enterprise Architecture; by Martin Fowler; page 496 (Special Case): Thay vì trả về null, hay giá trị linh tinh hãy trả về một trường hợp đặc biệt của interface mà người gọi mong muốn.

Chúng ta còn bất cứ lí do tốt nào để đặt câu hỏi về lời khuyên trên, và hỏi: Có phải trả về một tập hợp rỗng sẽ tốt hơn là giá trị null?

Lượn qua vài câu hỏi tương tự trên StackOverflow hay các diễn đàn, chúng ta cũng có thể thấy không phải ai cũng đồng ý với ý kiến trên. Có rất nhiều ý kiến khác, có những ý kiến thật sự trái ngược. Ví dụ như một câu trả lời cho câu hỏi Should functions return null or an empty object? (liên quan đến objects nói chung nhiều hơn là danh sách) được chú ý đưa ra luận điểm trái ngược:

             Trả về giá trị null luôn luôn là giải pháp tốt nhất.

Viết một hàm trả về giá trị tập hợp rất khổ biến. Nó xuất hiện ở rất cả các chương trình ứng dụng hay các ngôn ngữ lập trình. Mục đích của chuỗi những bài viết này đưa ra cái nhìn sâu hơn cho đề tài quan trọng và xuyên suốt này. Những cách tiếp cận được và chưa được? Có phải có một số lượng cố định luôn đúng trong tất cả các hướng tiếp cận? Có những trường hợp đặc biệt hay không? Và hướng tiếp cận nào bạn nên áp dụng vào mã nguồn của mình?

Xem nào.

Bài viết này dựa vào một bài Why We Should Love ‘null’.Bài viết trước tập trung vào trường hợp tổng quát là trả về một giá trị null cho hàm, bài này còn đưa ra trường hợp đặc biệt hơn là hàm được trả về một tâp hợp. Trong bài viết tôi cũng tham khảo một số vấn đề từ bài viết trên, nên bạn có thể đọc bài trên trước (hay tệ nhất cũng đọc phần tổng kết).

Tôi sẽ sử dụng qui ước list (danh sách) xuyên suốt bài viết. Nhưng nó bao hàm tất cả các kiểu về iteratables (lặp), enumerations (liệt kê) và collections (tập hợp) ví dụ như sets, lists, maps, dictionaries, arrays, strings, etc.

Một ví dụ đơn giản

Trước khi đi vào phân tích những trường hợp khác nhau, chúng ta lướt sơ qua một đoạn mã đơn giản để làm rõ nguyên nhân đằng sau lời khuyên “trả về một danh sách rỗng đừng trả về null”

Tôi sử dụng Java để làm những ví dụ trong bài. Tuy nhiên, bạn có thể sự dụng bất kì ngôn ngữ lập trình nào bạn thích. Nguyên lý chúng ta đang xem xét ứng dụng cho tất cả các ngôn ngữ thông dụng.

Hãy xem một hàm trả về một danh sách các ký tự số được tìm thấy trong một chuỗi:

Như chúng ta thấy, hàm trên làm theo đúng lời khuyên để không bao giờ trả về null. Nếu không có ký tự số nào được tìm thấy chuỗi đưa vào, một danh sách rỗng được trả về.

Để sử dụng hàm trên, chúng ta viết một đoạn mã:

Thực thi đoạn mã trên ta nhận được:

Mỗi lần hàm trên được gọi và trả về một danh sách rỗng, một danh sách rỗng mới được tạo ra. Để tránh trường hợp này, chúng ta nên trả về một shared and immutable rỗng. Sử dụng Collections.emptyList() để làm điều đó. Tuy nhiên, sẽ tốt hơn khi luôn luôn trả về danh sách không đổi(immutable). Trong ứng dụng thực tế, chúng ta sẽ thay thế:

… thành

Để hiểu lí do của lời khuyên “đừng trả về giá trị null”, hãy thay đổi hàm trên để nó trả về giá trị null nếu chuỗi không có một ký tự số nào:

Thực thi lại đoạn mã test chúng ta vẫn thấy một kết quả đúng:

Nhưng điều gì sẽ xảy ra nếu chuỗi không chứa bất kì một ký tự số nào, với đoạn mã ở dưới:

Một ngoại lệ NullPoiterException sẽ xảy ra, nguyên nhân ở digits.size(), bởi vì digits là null

Để tránh ngoại lệ xảy ra, chúng ta phải kiểm trả null trước và đoạn mã test sẽ trở thành:

Chúng ta cải tiến một ít:


Khi so sánh của 2 giải pháp (trả về danh sách rỗng và trả về null), điều đó trở nên rõ ràng tại sao nhiều developer đồng ý với lời khuyên “trả về một danh sách rỗng thay vì null”. Đoạn mã cho giải pháp đầu tiên ngắn hơn, đỡ đau tay hơn khi gõ mã :)), và làm đúng nhiệm vụ nó phải làm  mà không bị rủi ro của việc ném ra ngoại lệ NullPoiterException.

Vậy …

Vấn đề là gì?

Trước khi xem xét lỗ hổng nghiêm trọng và cạm bẫy của “trả về danh sách rỗng thay vì null”, đầu tiên chúng ta nhìn sâu hơn vấn đề tổng quát gây khó chịu cho nhiều programmers: những tài liệu chuẩn và đáng tin cậy của những APIs có hàm trả về danh sách.

Để làm rõ hơn vấn đề, chúng ta xem qua mô tả hàm của class java.io.File:

Hàm này được sử dụng để lấy tất cả tập tin trong một thư mục.Ví dụ, để liệt kê ra danh sách tập tin trong thư mục C:\Temp\, chúng ta có đoạn mã:

Một câu hỏi quan trọng được đặt ra ngay lập tức: Hàm trên sẽ trả ra gì nếu thư mục kia rỗng? Có phải nó trả về một danh sách rỗng hay null?

Chỉ nhìn vào mô tả của hàm, chịu bó tay. Trong thế giới Java (và nhiều môi trường lập trình khác), không hề có qui tắc chuẩn được áp dụng – như “luôn luôn trả về danh sách rỗng” hay “luôn luôn trả về null”. Một vài môi trường, đó có thể là một sự kiến nghị tổng quát hay một bài hướng dẫn cho dự án mà nên được áp dụng bởi tất cả programmers tham gia dự án, nhưng qui tắc đó không thể được kiểm tra và được bắt buộc bởi ngôn ngữ hay trình biên dịch – programmer có thể ngẫu nhiên vi phạm qui tắc đó. Ví dụ, Microsoft kiến nghị trong bài Guidelines for Collections là “KHÔNG trả về giá trị null từ những hàm trả về danh sách”. Nhưng điều đó không có nghĩa chúng ta có thể dựa vào nó. Có một thảo luận cho câu hỏi ở Stackoverflow Best explanation for languages without null như thế này

… Bất cứ khi nào tôi truy cập một biến kiểu tham chiếu trong .NET, tôi phải xem xét nó có thể null.

Thường nó sẽ không bao giờ có giá trị null, bởi vì các programmer đã cấu trúc cho đoạn mã để nó không bao giờ xảy ra. Nhưng trình biên dịch không quan tâm đến chuyện đó, và mỗi lần bạn thấy nó, bạn phải tự  đặt câu hỏi “nó có thể null không”? Có phải tôi cần kiểm tra giá trị null tại đây không?

Trong trường hợp lý tưởng, trong nhiều trường hợp giá trị null không có ý nghĩa, nó không nên được chấp nhận.

Có một ít rắc rối trong .NET, gần như mọi thứ đều có thể null. Bạn phải tin tưởng vào tác giả của mã bạn đang gọi hoặc bạn có thể đào sâu vào để kiểm tra mọi thứ.

(Nên đọc qua câu hỏi ở Stackoverflow để hiểu câu trả lời trên)

Do vậy, để biết được listFiles() trả ra gì nếu thư mục rỗng, bạn nên:

  • tra tài liêu của hàm đó (nếu nó có và đã được cập nhập mới nhất)
  • viết một đoạn chương trình test nhanh để tìm ra giá trị được trả về khi thư mục rỗng.
  • đào sâu vào mã nguồn của JDK (i.e. file src.zip trong thư mục gốc JDK)

Với một lớp chuẩn trong java, trường hợp này chúng ta may mắn. Tài liệu đã có sẵn và đây là câu trả lời từ Java 8 online API documentation:

Trả về: Một mảng tên đường dẫn trừu tượng để biểu thị cho các tập tin và thư mục trong thư mục được truyền vào. Mảng này sẽ rỗng nếu thư mục đó rỗng. Trả về null nếu đường dẫn thư mục truyền vào không biểu thị đúng thư mục hay nếu một lỗi về I/O xảy ra.

Ném ra ngoại lệ SecurityException nếu một trình quản lý bảo mật đang tồn tại và hàm SecurityManager.checkRead(String) từ chối truy cập vào thư mục.

Okie, bây giờ chúng ta biết chúng ta phải kiểm thử cho  một mảng rỗng  hay một giá trị null, ví dụ:

Để giữ cho ví dụ trên được đơn giản, tôi đã không lồng vào đoạn try/catch để bắt ngoại lệ SecurityException và tôi đã không xem xét trong tường hợp lỗi I/O, hàm trên sẽ trả về giá trị null thay vì quăng ra một ngoại lệ.

Đôi khi chúng ta không được may mắn. Không có một tài liệu nào báo cho chúng ta biết nếu một danh sách rỗng hay một giá trị null được trả về. Và chúng ta không có thời gian để viết một đoạn mã kiểm thử cho mọi hàm trả về một danh sách hay để tiếp cận được cách thực thi của hàm đó (nếu chúng ta truy cập trực tiếp vào mã nguồn)

Và trong những trường hợp như vậy, cách tốt nhất là chúng ta luôn kiểm tra giá trị null và danh sách rỗng. Nhưng có một ít khó chịu là làm giảm tốc độ thực thi chương trình bởi cả 2 trường hợp kiểm thử (kiểm trả null và kiểm tra danh sách rỗng) là không cần thiết nếu hàm trên không bao gờ trả về danh sách rỗng và cũng không bao giờ trả về null.

Dù sao đi nữa thì đó cũng là điều chúng ta nên làm. Nhưng chuyện gì xảy ra trong thực tế? Một số programmers thật sự có kiểm tra danh sách rỗng và giá trị null. Một số thì chỉ kiểm tra giá trị rỗng, một số thì chỉ kiểm tra giá trị null, còn lại chấp hết, không kiểm tra gì ?

Điều đó thật sự nasty (bỉ bổi, không biết ý tác giả sao hehee), nhưng hãy ở lại với tôi đã. Chúng ta sẽ tìm ra một giải pháp đơn giản thôi. Hứa luôn!

Giả sử rằng một hàm có thể trả về một danh sách rỗng hay giá trị null hay – tại sao không – lúc thì danh sách rỗng lúc thì null tuỳ vào trạng thái của hệ thống. Một giả sử nữa là không có gì cản trở các lập trình làm đúng hay sai, thích làm gì thì làm :D. Sau đó chúng ta kết luận một bộ các sự kết hợp có thể xảy ra một cách ấn tượng. Một câu hỏi thú vị được đưa ra: Điều gì sẽ xảy ra với mỗi trường hợp nếu hàm trả về “không dữ liệu” và chúng ta thật sự sử dụng kết quả đó, ví dụ để lấy một số phần tử trong một danh sách.

Kết quả sẽ được hiển thị bên bản dưới.

 Đoạn mã kiểm tra cho trường hợp

“không có dữ liệu”

Kết quả mà hàm trả về
null empty list
nothing null pointer error undefined
if (list.isEmpty()) null pointer error correct execution
if (list == null) correct execution undefined
if (list.isEmpty() || list == null) null pointer error correct execution
if (list == null || list.isEmpty()) correct execution correct execution

+ null pointer error: bị lỗi null pointer exception.

+ undefined: không xác định được.

+ correct execution: chạy tốt.

Chú thích (lượt dịch): Chúng ta có thể thêm rất nhiều trường hợp khác nhau vào bảng trên nhưng thật sự không cần thiết, bảng trên đã bao gồm tất cả trường hợp khả thi cho bài thảo luận này.

Có một vài điểm khá thú vị để làm rõ vấn đề ở bảng trên:

  • Để cho mã làm việc được cho tất cả các trường hợp ở trên là chúng ta phải kiểm tra danh sách rỗng và cho giá trị null. Nhưng việc đó mang lại một số bất lợi nghiệm trọng: đó là thời gian. Vướng viếu khi viết mã lệnh. Chúng ta phải cẩn trọng trong việc kiểm tra giá trị null trước rồi mới kiểm tra isEmpty(). Và chúng ta phải sử dụng đúng toán tử or (i.e || và không phải |). Chúng ta có thể giảm bớt việc vướng viếu này bằng cách sử dụng những hàm có sẵn hoặc tự viết cho việc kiểm tra này (kiểm tra đồng thời null và empty). Ví dụ, kiểm tra một chuỗi string, trong C# chúng ta có String.IsNullOrEmpty() và trong Java chúng ta có thể dùng com.google.common.base.Strings.isNullOrEmpty() (from Google Guava) hay org.apache.commons.lang3.StringUtils.isEmpty() (from Apache Commons).
  • Nếu chúng ta sống trong thế giới lý tưởng nơi
    or
    sau đó đoạn mã lệnh sẽ trở lên chính xác. (Chỗ này tác giả chơi chữ or là trong đoạn mã check null và empty cùng lúc ở bảng trên)Nhưng chúng ta lại không sống trong một thế giới hoàn hảo. Ngoài ra, viết đoạn mã chính xác (i.e no bugs) không nhất thiết là viết đoạn mã đó tốt nhất, bởi vì còn có những nhân tố khác như hiệu xuất, sử dụng bộ nhớ và khả năng bảo trì cũng rất quan trọng.
  • Tất cả các hàm trả về danh sách rỗng và tất cả mã lệnh đều phải kiểm trả danh sách rỗng.
  • Tất cả các hàm trả về null và tất cả mã lệnh phải kiểm tra giá trị null.
  • Điểm thích đang nhất ở đây là:
    Ngữ cảnh “undifined” có nghĩa là kết quả sẽ không dự đoán được và có thể biến đổi từ hoàn toàn vô hại sáng vô cùng có hại. Chúng ta sẽ nhìn vào các ví dụ ở các phần sau.Đây là vấn đề mấu chốt.

Tất cả chúng ta đều muốn viết một đoạn mã chất lượng cao (:D) và không có bug trong một thời gian ngắn nhất có thể. Vì vậy, điều mà chúng ta muốn hỏi lúc này là:

Hướng tiếp cận nào nói chung là tốt hơn? Hướng dẫn đến lỗi null pointer trong trường hợp có bug hay hướng tiếp cận dẫn đến kết quả “không xác định” (undefined)? 

Câu trả lời sẽ có ở phần tiếp theo trong chuỗi các bài viết về vấn đề này. Chúng ta sẽ so sánh 2 hướng tiếp cận bằng cách nhìn vào một vài đoạn mã và chúng ta sẽ xem xét khả năng dễ đọc của chương trình, yêu cầu về thời gian và không gian, cũng như sự khác biệt của các API. Sau đó, chúng ta sẽ nhìn vào việc sử dụng empty list (danh sách rỗng) trong thực tế. Chúng có thực sự tồn tại? Chúng được sử dụng như thế nào? Kết quả sẽ có thể sẽ làm bạn ngạc nhiên đấy.

  • Nếu hàm trả về null và mã lệnh có sai sót, thì kết quả luôn luôn là lỗi null pointer.
  • Nếu hảm trả về danh sách rỗng và mã có sai sót, thì kết quả luôn luôn là “không xác định”

Vậy là xong phần 1 trong loạt bài viết về việc chúng ta nên trả về danh sách rỗng (empty list) hay null khi chúng ta viết một hàm trả về một kiểu tập hợp. Mình sẽ cố gắng dịch hết chuỗi bài này trong thời gian sớm nhất có thể. Mong mọi nguời đón đọc. Cảm ơn các bạn đã ghé thăm và có gì sai sót mong các bạn comment để mình chỉnh sửa.

Chào mừng đến với phần thứ 2 của loạt bài : “Có thật sự tốt hơn khi trả về danh sách rỗng thay vì trả về null?” Phần 1 đã giới thiệu về lời khuyên phổ biến “trả về danh sách rỗng thay vì null”, lời khuyên được chấp nhận rỗng rãi bởi các leader (đứng đầu, quản lý nhóm) trong ngành công nghiệp phần mềm. Một số mã lệnh mẫu mà chúng ta đã thấy giải thích lý do vì sao như vậy. Cuối cùng, có một gợi ý rằng lời khuyên trên nên được xem xét lại.

Trong phần này, chúng ta sẽ sẽ so sánh cả 2 cách tiếp cận (i.e, trả về danh sách rỗng hay trả về null) bằng cách nhìn vào một số mã lệnh mẫu cụ thể của cả 2 cách tiếp cận và xem xét điểm thuận và bất lợi của chúng.

Phần II: So sánh 2 cách tiếp cận

2 cách tiếp cận đối ngược
Rất nhiều hàm trong lập trình phần mềm trả về tập hợp (sets, lists, maps, arrays, strings, v.v…). Câu hỏi đặt ra mà chúng ta quan tâm là: Hàm nên trả về gì nếu không có dữ liệu để trả về. Ví dụ, hàm nên trả về gì nếu không có trẻ em nào trong lớp học:

Chúng ta sẽ so sánh 2 cách tiếp cận:

  • Trả về một tập hợp rỗng
    Chúng ta gọi cách này là cách tiếp cận tránh-null (avoid-null)
  • Trả về null
    Chúng ta gọi cách này là cách tiếp cận yêu-null (love-null =)))

Có những cách tiếp cận khác đang tồn tại, nhưng chúng ta sẽ không thảo luận về chúng. Ví dụ, chúng ta có thể sử dụng Optional/Maybe pattern(mẫu). Mẫu này đã được thảo luận trong bài viết trước “Why should love-null” (tại sao chúng ta yêu null).

Hơn nữa, trong phần này, chúng ta sẽ không xem xét trường hợp ném ra ngoại lệ nếu phép toán của hàm không thể thực thi chính xác. (viết tào lao mà vẫn chạy)

Mục đích này giúp cho mọi thứ đơn giản và chúng ta chỉ tập trung vào việc trả về danh sách rỗng đối lập với trả về null.

Với mỗi cách tiếp cận, chúng ta sẽ xem xét:

  • Tính dễ đọc của phần mềm.
  • Thời gian và không gian.
  • Những sự khác nhau của API.

Xem xét về vấn đề dễ đọc của phần mềm

Mục tiêu của chương này là trả lời câu hỏi:
Có phải một trong 2 cách tiếp cận giúp chúng ta viết một phần mềm dễ đọc không?

Hay:

Có phải chúng ta tăng độ rủi ro có nhiều bugs nếu chúng ta chọn cách này mà không phải cách khác không?

Nào chúng ta cùng bắt đầu với một thí dụ đơn giản thật sự nào.

Liệt kê phần tử trong một tập hợp
Giả dụ chúng ta muốn in ra một danh sách đơn đặt hàng của khách hàng. Chúng ta sẽ sử dụng hàm bên dưới để lấy ra tất cả các đơn đặt hàng của khách hàng đã cho.

Để in ra danh sách, chúng ta có thể viết một hàm như sau:

Nếu các đơn đặt hàng tồn tại với khách hãng có mã “123”, thì kết quả sẽ thế này:

Oh mọi thứ đang kun [cool :)] (so far so good)

Nhưng điều gì sẽ xảy ra nếu không có đơn hàng nào của khách hàng đó?

Trong thế giới tẩy chay-null (avoid-null), getOrdersByCustomer() trả về một danh sách rỗng. Vì vậy, mã lệnh của chúng ta vẫn chạy và không in ra gì.

Trong thế giới sùng bái-null (love-null) getOrdersByCustomer() trả về null và điều này gây ra NullPointerException được ném ra trong đoạn lệnh for.

Và đó chính xác là điều tại sao những người ủng hộ avoid-null tránh trả về null và trả về danh sách rỗng. Họ cho rằng:

Mã lệnh của bạn đơn giản và làm việc ở tất cả các tình huống. Bạn không phải kiểm tra giá trị null và loại bỏ rủi ro cho lỗi null pointer. Và trong trường hợp này điều bạn thật sự cần làm là kiểm tra “không có dữ liệu”, bạn sử dụng isEmpty() (e.g if (list.isEmpty()) {}).

Tóm cái đuôi lại là: mã lệnh sẽ đơn giản và ít gặp lỗi dễ mắc phải.

Tuy nhiên, bây giờ chúng ta xem xét từ phía nguời dùng cuối (end-user). Người dùng sẽ nhìn nhận (experience) trường hợp ‘không dữ liệu’ như thế nào?

Trong một phiên bản phức tạp hơn cho chương trình, người dùng có thể nhập vào id của khách hàng. Họ nhập “123” and đợi kết quả.

Trong thế giới love-null, ứng dụng sẽ bị chết bởi lỗi null pointer. Thật tồi tệ (That’s not good).

Trong thế giới avoid-null, một trong các trường hợp sau sẽ xảy ra, tuỳ thuộc vào hết quả hiển thị là gì:

  • Không có gì hiển thị
  • Trình duyệt hiển thị một bảng rỗng hay
  • Một trang giấy trắng được in ra.

Người dùng sẽ ngạc nhiên và tự hỏi. Có chuyện gì xảy ra, có phải không có đơn hàng nào của khách hàng này hay có chuyện gì xảy ra mới cái máy tính?

Chẳng biết được điều này gây ít bực mình hơn trường hợp chương trình chết hay không.

Tuy nhiên, người dùng thật sự muốn là có một thông điệp rõ ràng và dễ hiểu giống như:

Ghi chú: Tôi lấy ý tưởng cho ví dụ này bởi tôi trải qua tình huống này (như người dùng cuối) vài tuần trước đây. Tôi muốn xem danh sách của các tay đua trong đội trong cuộc đua Tour de France đang đến (một cuộc đua xe đẹp rất nổi tiếng diễn ra ở France mỗi năm). Vì vậy, tôi truy cập vào một web site trên internet để có thể xem danh sách của tất cả các nhóm. Bên trái tên mỗi đội có một nút để có phóng lớn và xem danh sách các tay đua trong đội. Tôi nhấn vào nút này nhưng không có gì xảy ra. Tôi có thể thấy là  khoảng cách giữa tên đội mà tôi nhấn và bên dưới tên đội được tăng lên một ít. Nên tôi đoán rằng các tay đua chưa được xác định. Chương trình chắc chắn đã trả về một danh sách rỗng và được hiển thị trong một bảng rỗng. Đó là tại sao tôi thấy trang di chuyển một ít nhưng không có dữ liệu hiện thị lên. Cái tôi mong đợi là một thông điệp dễ hiểu rằng không xác định được tay đua nào.

Trong cả 2 cách tiếp cận, khá dễ dàng để hiển thị một thông điệp thích hợp.

Trong thế giới avoid-null, chúng ta cải tiến đoạn mã như sau:

Trong thế giới love-null, đoạn mã trở thành:

Như bạn có thể thấy, điểm khác biệt duy nhất cho việc kiểm tra ‘không có dữ liệu’ là:


đối lập với:


Đây chỉ là một ví dụ nho nhỏ, nhưng có một bài học được rút ra:

Trường hợp ‘không dữ liệu’ về ngữ nghĩa khác so với trường hợp ‘một hoặc nhiều phần tử được tìm thấy’

Đây là điểm cốt yếu, tuy chả có gì đáng ngạc nhiên về điều đó (phát biểu trên). Trong thực tế, chúng ta thường tạo ra điều khác biệt này suốt và phản ứng thích hợp, ví dụ:

  • Trong trường hợp ‘lớp học trống rỗng’ rất khác với trường hợp ‘có một hoặc nhiều sinh viên trong lớp học’. Trong trường hợp đầu tiên, không cần giáo viên vào dạy, cửa sổ nên được đóng lại, đèn tắt, v.v…
  • Trong trường hợp ‘không có khách hàng phàn nàn” rất khác với trường hợp ‘một vài khách hàng báo cáo lỗi của chương trình’. Trong trường hợp đầu tiên, chúng ta có thể thư giản, luớt net, tám với mẹ (call mum), thích làm gì làm. Trong trường hợp thứ 2, chịu thua.
  • v.v và v.v

(cont)

Như các bạn thấy mình dịch khá tệ, dịch từng từ nên khó truyền đạt hết hàm ý của các giả. Mình chỉ cố gắng cung cấp nhiều nhất từ khóa có thể để mọi người tìm kiếm. Mình khuyên các bạn nên đọc bài gốc tiếng Anh.

Got something to say? Go for it!