Sử dụng đúng
Có nhiều cách sử dụng khác nhau null
. Cách phổ biến nhất và đúng về mặt ngữ nghĩa là sử dụng nó khi bạn có thể có hoặc không có một giá trị duy nhất . Trong trường hợp này, một giá trị bằng null
hoặc là một cái gì đó có ý nghĩa như một bản ghi từ cơ sở dữ liệu hoặc một cái gì đó.
Trong những tình huống này, sau đó bạn chủ yếu sử dụng nó như thế này (bằng mã giả):
if (value is null) {
doSomethingAboutIt();
return;
}
doSomethingUseful(value);
Vấn đề
Và nó có một vấn đề rất lớn. Vấn đề là vào thời điểm bạn gọi doSomethingUseful
giá trị có thể chưa được kiểm tra null
! Nếu không thì chương trình có thể sẽ bị sập. Và người dùng thậm chí có thể không nhìn thấy bất kỳ thông báo lỗi tốt nào, bị bỏ lại với một cái gì đó như "lỗi khủng khiếp: giá trị mong muốn nhưng không có giá trị!" (sau khi cập nhật: mặc dù có thể có ít thông tin lỗi hơn như Segmentation fault. Core dumped.
, hoặc tệ hơn nữa, không có lỗi và thao tác không chính xác trên null trong một số trường hợp)
Quên viết séc null
và xử lý các null
tình huống là một lỗi cực kỳ phổ biến . Đây là lý do tại sao Tony Hoare, người đã phát minh ra null
tại một hội nghị phần mềm có tên QCon London năm 2009 rằng ông đã mắc sai lầm hàng tỷ đô la vào năm 1965:
https://www.infoq.com/presentations/Null-References-The-Billion-Dollar- Sai lầm-Tony-Hoare
Tránh vấn đề
Một số công nghệ và ngôn ngữ giúp kiểm tra null
không thể quên theo các cách khác nhau, giảm số lượng lỗi.
Ví dụ, Haskell có Maybe
đơn nguyên thay vì null. Giả sử đó DatabaseRecord
là một loại do người dùng định nghĩa. Trong Haskell, một giá trị của loại Maybe DatabaseRecord
có thể bằng Just <somevalue>
hoặc nó có thể bằng Nothing
. Sau đó, bạn có thể sử dụng nó theo nhiều cách khác nhau nhưng cho dù bạn sử dụng nó như thế nào, bạn không thể áp dụng một số thao tác trên Nothing
mà không biết.
Ví dụ, hàm này được gọi là zeroAsDefault
trả về x
cho Just x
và 0
cho Nothing
:
zeroAsDefault :: Maybe Int -> Int
zeroAsDefault mx = case mx of
Nothing -> 0
Just x -> x
Christian Hackl nói C ++ 17 và Scala có cách riêng của họ. Vì vậy, bạn có thể muốn thử tìm hiểu xem ngôn ngữ của bạn có bất cứ thứ gì như vậy không và sử dụng nó.
Nulls vẫn được sử dụng rộng rãi
Nếu bạn không có gì tốt hơn thì sử dụng null
là tốt. Chỉ cần tiếp tục xem ra cho nó. Gõ khai báo trong các hàm sẽ giúp bạn phần nào.
Ngoài ra điều đó nghe có vẻ không tiến bộ lắm nhưng bạn nên kiểm tra xem đồng nghiệp của mình có muốn sử dụng null
hay không. Họ có thể bảo thủ và có thể không muốn sử dụng cấu trúc dữ liệu mới vì một số lý do. Ví dụ, hỗ trợ các phiên bản cũ hơn của một ngôn ngữ. Những điều như vậy nên được khai báo trong các tiêu chuẩn mã hóa của dự án và thảo luận đúng đắn với nhóm.
Theo đề nghị của bạn
Bạn đề nghị sử dụng một trường boolean riêng. Nhưng dù sao bạn cũng phải kiểm tra nó và vẫn có thể quên kiểm tra nó. Vì vậy, không có gì chiến thắng ở đây. Nếu bạn thậm chí có thể quên một cái gì đó khác, như cập nhật cả hai giá trị mỗi lần, thì điều đó còn tồi tệ hơn. Nếu vấn đề quên kiểm tra null
không được giải quyết thì không có điểm nào. Tránh null
là khó khăn và bạn không nên làm theo cách làm cho nó tồi tệ hơn.
Làm thế nào để không sử dụng null
Cuối cùng, có những cách phổ biến để sử dụng null
không chính xác. Một cách như vậy là sử dụng nó thay cho các cấu trúc dữ liệu trống như mảng và chuỗi. Một mảng trống là một mảng thích hợp như bất kỳ khác! Nó hầu như luôn luôn quan trọng và hữu ích cho các cấu trúc dữ liệu, có thể phù hợp với nhiều giá trị, có thể để trống, tức là có 0 độ dài.
Từ quan điểm đại số, một chuỗi rỗng cho các chuỗi giống như 0 cho các số, tức là nhận dạng:
a+0=a
concat(str, '')=str
Chuỗi rỗng cho phép các chuỗi nói chung trở thành một chuỗi đơn:
https://en.wikipedia.org/wiki/Monoid
Nếu bạn không hiểu thì điều đó không quan trọng đối với bạn.
Bây giờ hãy xem tại sao nó quan trọng để lập trình với ví dụ này:
for (element in array) {
doSomething(element);
}
Nếu chúng ta vượt qua một mảng trống ở đây, mã sẽ hoạt động tốt. Nó sẽ không làm gì cả. Tuy nhiên, nếu chúng tôi vượt qua null
ở đây thì có thể chúng tôi sẽ gặp sự cố với một lỗi như "không thể lặp qua null, xin lỗi". Chúng tôi có thể bọc nó if
lại nhưng ít sạch hơn và một lần nữa, bạn có thể quên kiểm tra nó
Cách xử lý null
Những gì doSomethingAboutIt()
nên làm và đặc biệt là liệu nó có nên ném một ngoại lệ hay không là một vấn đề phức tạp khác. Nói tóm lại, nó phụ thuộc vào việc null
giá trị đầu vào có thể chấp nhận được đối với một tác vụ nhất định hay không và điều gì được mong đợi trong phản hồi. Các ngoại lệ dành cho các sự kiện không được mong đợi. Tôi sẽ không đi sâu hơn vào chủ đề đó. Câu trả lời này rất lâu rồi.
std::optional
hoặcOption
. Trong các ngôn ngữ khác, bạn có thể phải tự xây dựng một cơ chế phù hợp hoặc bạn thực sự có thể dùng đếnnull
hoặc một cái gì đó tương tự vì nó thành ngữ hơn.