Là tốt hơn để trả về NULL hoặc các giá trị trống từ các hàm / phương thức không có giá trị trả về?


135

Tôi đang tìm kiếm một đề nghị ở đây. Tôi đang đấu tranh với việc tốt hơn là trả về NULL hoặc một giá trị trống từ một phương thức khi không có giá trị trả về hoặc không thể xác định được.

Lấy hai phương pháp sau đây làm ví dụ:

string ReverseString(string stringToReverse) // takes a string and reverses it.
Person FindPerson(int personID)    // finds a Person with a matching personID.

Trong ReverseString(), tôi sẽ nói trả về một chuỗi rỗng vì kiểu trả về là chuỗi, vì vậy người gọi đang mong đợi điều đó. Ngoài ra, theo cách này, người gọi sẽ không phải kiểm tra xem liệu NULL có được trả lại hay không.

Trong FindPerson(), trả lại NULL có vẻ như phù hợp hơn. Bất kể có trả lại NULL hay đối tượng Person ( new Person()) trống hay không, người gọi sẽ phải kiểm tra xem liệu Object Object có phải là NULL hay không trước khi thực hiện bất kỳ điều gì với nó (như gọi UpdateName()). Vậy tại sao không trả lại NULL ở đây và sau đó người gọi chỉ phải kiểm tra NULL.

Có ai khác đấu tranh với điều này? Bất kỳ trợ giúp hoặc cái nhìn sâu sắc được đánh giá cao.


2
Nó sẽ thay đổi. Bạn cũng có thể lập luận rằng phương thức sẽ dừng các giá trị không chính xác lên phía trước, ví dụ, tham số stringToReverse của bạn được truyền vào dưới dạng rỗng hoặc null. Nếu bạn thực hiện kiểm tra đó (ném lại một ngoại lệ), nó sẽ luôn trả về một cái gì đó không phải là null.
Aaron McIver

Fowler POEAA - trang. 496 Các mẫu cơ sở - Trường hợp đặc biệt (đối tượng null) Bloch Java hiệu quả, Phiên bản thứ 2 Mục 43: Trả về các mảng hoặc bộ sưu tập trống, không phải là null
Boris Treukhov 17/11/11

1
@Boris Tôi không thấy bình luận của bạn. Tôi thực sự chỉ đăng Trường hợp đặc biệt như một câu trả lời.
Thomas Owens

@Thomas Tôi không thấy có vấn đề gì trong đó :). Đối với câu hỏi đó là vấn đề của lẽ thường.
Boris Treukhov

Một nhận xét thú vị là trong cơ sở dữ liệu của Oracle, NULL và chuỗi trống giống nhau
WuHoUnited

Câu trả lời:


77

StackOverflow có một cuộc thảo luận tốt về chủ đề chính xác này trong phần hỏi đáp này . Trong câu hỏi được xếp hạng hàng đầu, kronoz lưu ý:

Trả về null thường là ý tưởng tốt nhất nếu bạn có ý định chỉ ra rằng không có dữ liệu nào khả dụng.

Một đối tượng trống ngụ ý dữ liệu đã được trả về, trong khi trả về null chỉ rõ rằng không có gì được trả về.

Ngoài ra, trả về null sẽ dẫn đến ngoại lệ null nếu bạn cố truy cập các thành viên trong đối tượng, điều này có thể hữu ích để làm nổi bật mã lỗi - cố gắng truy cập vào một thành viên không có ý nghĩa gì. Truy cập các thành viên của một đối tượng trống sẽ không thất bại có nghĩa là lỗi có thể không được phát hiện.

Cá nhân, tôi muốn trả về các chuỗi trống cho các hàm trả về các chuỗi để giảm thiểu số lượng xử lý lỗi cần được đặt đúng chỗ. Tuy nhiên, bạn sẽ cần đảm bảo rằng nhóm mà bạn hợp tác sẽ tuân theo cùng một quy ước - nếu không thì lợi ích của quyết định này sẽ không đạt được.

Tuy nhiên, như người đăng trong câu trả lời SO đã lưu ý, có lẽ nên trả về null nếu một đối tượng được mong đợi để không có nghi ngờ gì về việc liệu dữ liệu có được trả lại hay không.

Cuối cùng, không có cách tốt nhất để làm việc. Xây dựng sự đồng thuận nhóm cuối cùng sẽ thúc đẩy các thực tiễn tốt nhất của nhóm của bạn.


8
Cá nhân, tôi muốn trả về các chuỗi trống cho các hàm trả về các chuỗi để giảm thiểu số lượng xử lý lỗi cần được đặt đúng chỗ. Không bao giờ hiểu lập luận này. Kiểm tra null là gì, nhiều nhất <10 ký tự? Tại sao chúng ta hành động như thế này là lao động uốn cong trở lại?
Aaron McIver

19
Không ai nói đó là lao động uốn éo. Nhưng đó lao động. Nó thêm một trường hợp đặc biệt vào mã có nghĩa là thêm một nhánh để kiểm tra. Thêm vào đó, nó làm gián đoạn dòng chảy của mã. for x in list_of_things() {...}được cho là nhanh hơn để mò mẫm sau đól = list_of_things(); if l != null {...}
Bryan Oakley

5
@Bryan: có một sự khác biệt lớn giữa danh sách giá trị trống và chuỗi rỗng. Một chuỗi hiếm khi đại diện cho một danh sách các ký tự.
kevin cline

3
@kevin cline: tất nhiên rồi. Nhưng trong trường hợp của một chuỗi, bạn có thể muốn chuỗi đó xuất hiện trong nhật ký cho dù đó là null hay không. Phải kiểm tra xem nó có rỗng không để bạn có thể in một chuỗi trống làm xáo trộn ý định thực sự. Hoặc có lẽ nó giống như trường cơ sở dữ liệu chứa nội dung do người dùng đóng góp mà bạn muốn thêm vào trang web. Điều đó dễ thực hiện emit(user.hometown)hơnif user.hometown == null { emit("") else {emit(user.hometown)}
Bryan Oakley

1
Nó không chỉ là gõ thêm (không phải là một vấn đề lớn), mà còn ảnh hưởng đến khả năng đọc. Cá nhân tôi tìm thấy mã với một loạt các kiểm tra null làm mất tập trung.
ToddR

82

Trong tất cả các mã tôi viết, tôi tránh trở về nulltừ một hàm. Tôi đọc nó trong Clean Code .

Vấn đề với việc sử dụng nulllà người sử dụng giao diện không biết liệu đó có phải nulllà kết quả có thể xảy ra hay không và liệu họ có phải kiểm tra hay không, vì không có not nullloại tham chiếu nào .

Trong F # bạn có thể trả về một optionloại, có thể là some(Person)hay none, vì vậy nó là hiển nhiên đối với người gọi rằng họ phải kiểm tra.

Mẫu C # (chống) tương tự là Try...phương thức:

public bool TryFindPerson(int personId, out Person result);

Bây giờ tôi biết mọi người đã nói rằng họ ghét các Try...mẫu vì có một tham số đầu ra phá vỡ ý tưởng của một hàm thuần túy, nhưng nó thực sự không có gì khác hơn:

class FindResult<T>
{
   public FindResult(bool found, T result)
   {
       this.Found = found;
       this.Result = result;
   }

   public bool Found { get; private set; }
   // Only valid if Found is true
   public T Result { get; private set;
}

public FindResult<Person> FindPerson(int personId);

... Và thành thật mà nói, bạn có thể giả sử rằng mọi lập trình viên .NET đều biết về Try...mẫu này vì nó được sử dụng bên trong bởi .NET framework. Điều đó có nghĩa là họ không phải đọc tài liệu để hiểu những gì nó làm, điều này quan trọng với tôi hơn là tuân theo quan điểm của một số chủ nghĩa thuần túy (hiểu đó resultlà một outtham số, không phải là reftham số).

Vì vậy, tôi đi cùng TryFindPersonbởi vì bạn dường như cho thấy nó hoàn toàn bình thường không thể tìm thấy nó.

Mặt khác, nếu không có lý do hợp lý nào mà người gọi sẽ cung cấp personIdmà không tồn tại, thì tôi có thể sẽ làm điều này:

public Person GetPerson(int personId);

... và sau đó tôi sẽ ném một ngoại lệ nếu nó không hợp lệ. Các Get...tiền tố ngụ ý rằng người gọi biết điều đó nên thành công.


28
+1, tôi sẽ trả lời câu hỏi này với tham chiếu đến Clean Code nếu bạn chưa làm như vậy. Tôi thích câu thần chú: "NẾU CHỨC NĂNG CỦA BẠN CẦN LÀM GÌ TÊN NÓ NÓ, SAU ĐÓ LÀ MỘT NGOẠI LỆ." Muuuch tốt hơn là kiểm tra null mỗi chục dòng hoặc hơn ....
Graham

13
Tôi đã từ bỏ giả định bất cứ điều gì về kiến ​​thức của mọi người.
CaffGeek

5
"Vấn đề với việc sử dụng null là người sử dụng giao diện không biết liệu null có phải là kết quả có thể xảy ra hay không và liệu họ có phải kiểm tra hay không, bởi vì không có loại tham chiếu null." Vì các loại tham chiếu có thể là null, bạn có thể luôn cho rằng null là kết quả có thể xảy ra không?
Tommy Carlier

4
Nếu một hàm trả về một con trỏ (C / C ++) hoặc tham chiếu đến một đối tượng (Java), tôi luôn cho rằng nó có thể trả về null.
Giorgio

7
"Vấn đề với việc sử dụng null là người sử dụng giao diện không biết liệu null có phải là kết quả có thể xảy ra hay không và liệu họ có phải kiểm tra hay không, [...]" Nếu một phương thức có thể trả về null, thì rõ ràng là nên được đề cập như một phần của hợp đồng API.
Zsolt Török

28

Bạn có thể thử mẫu Trường hợp đặc biệt của Martin Fowler , từ Paterns of Enterprise Application Architecture :

Nulls là những thứ vụng về trong các chương trình hướng đối tượng vì chúng đánh bại tính đa hình. Thông thường, bạn có thể gọi foo một cách tự do trên một tham chiếu biến đổi của một loại nhất định mà không cần lo lắng về việc mục đó là loại chính xác hay là một lớp con. Với một ngôn ngữ được gõ mạnh, bạn thậm chí có thể có trình biên dịch kiểm tra xem cuộc gọi có đúng không. Tuy nhiên, vì một biến có thể chứa null, bạn có thể gặp phải lỗi thời gian chạy bằng cách gọi một thông báo trên null, điều này sẽ giúp bạn theo dõi ngăn xếp thân thiện, đẹp mắt.

Nếu một biến có thể là null, bạn phải nhớ bao quanh nó bằng mã kiểm tra null để bạn sẽ làm đúng nếu có null. Thường thì điều đúng là giống nhau trong nhiều ngữ cảnh, vì vậy cuối cùng bạn sẽ viết mã tương tự ở nhiều nơi - phạm tội sao chép mã.

Nulls là một ví dụ phổ biến của các vấn đề như vậy và những người khác mọc lên thường xuyên. Trong các hệ thống số, bạn phải đối phó với vô cực, trong đó có các quy tắc đặc biệt cho những thứ như phép cộng phá vỡ các bất biến thông thường của số thực. Một trong những kinh nghiệm đầu tiên của tôi về phần mềm kinh doanh là với một khách hàng tiện ích chưa được biết đến đầy đủ, được gọi là "người thuê nhà". Tất cả những điều này ngụ ý thay đổi hành vi thông thường của loại.

Thay vì trả về null hoặc một số giá trị lẻ, hãy trả về Trường hợp đặc biệt có giao diện giống như những gì người gọi mong đợi.


Tôi chạy vào kịch bản này, nhưng chỉ sau khi phần mềm được phát hành. Dữ liệu thử nghiệm của tôi không bao giờ kích hoạt nó, vì vậy nó có một chút lạc lõng và một nỗi đau để gỡ lỗi. Mặc dù tôi đã không giải quyết nó với mẫu Trường hợp đặc biệt, thật tốt khi biết về nó.
samis

14

Tôi nghĩ rằng ReverseString()sẽ trả về chuỗi đảo ngược và nó sẽ ném IllegalArgumentExceptionnếu được truyền vào a Null.

Tôi nghĩ FindPerson()nên theo Mô hình NullObject hoặc đưa ra một ngoại lệ không được kiểm soát về việc không tìm thấy thứ gì đó nếu bạn luôn có thể tìm thấy thứ gì đó.

Phải đối phó với Nullđiều đó là điều nên tránh. Có Nullngôn ngữ đã được nhà phát minh của nó gọi là Sai lầm hàng tỷ đô la !

Tôi gọi đó là sai lầm tỷ đô của tôi. Đó là phát minh của tài liệu tham khảo null vào năm 1965. Vào thời điểm đó, tôi đang thiết kế hệ thống loại toàn diện đầu tiên cho các tài liệu tham khảo bằng ngôn ngữ hướng đối tượng (ALGOL W).

Mục tiêu của tôi là đảm bảo rằng tất cả việc sử dụng tài liệu tham khảo phải tuyệt đối an toàn, với việc kiểm tra được thực hiện tự động bởi trình biên dịch. Nhưng tôi không thể cưỡng lại sự cám dỗ để đưa vào một tài liệu tham khảo null, đơn giản vì nó rất dễ thực hiện.

Điều này đã dẫn đến vô số lỗi, lỗ hổng và sự cố hệ thống, có thể gây ra hàng tỷ đô la đau đớn và thiệt hại trong bốn mươi năm qua.

Trong những năm gần đây, một số máy phân tích chương trình như PREfix và PREfast trong Microsoft đã được sử dụng để kiểm tra các tài liệu tham khảo và đưa ra cảnh báo nếu có rủi ro chúng có thể không có giá trị. Các ngôn ngữ lập trình gần đây hơn như Spec # đã đưa ra các khai báo cho các tham chiếu không null. Đây là giải pháp, mà tôi đã từ chối vào năm 1965.

Tony Hoare


11

Trả lại một lựa chọn . Tất cả lợi ích của việc trả về một giá trị không hợp lệ khác nhau (như có thể có các giá trị trống trong các bộ sưu tập của bạn) mà không có bất kỳ rủi ro nào về NullPulumException.


1
Hấp dẫn. Làm thế nào để thực hiện một tùy chọn trong Java? Và trong C ++?
Giorgio

1
@Giorgio, Đối với Java, các thư viện Guava, từ Google, có một lớp gọi là Tùy chọn .
Otavio Macedo

1
Đối với C ++, cóboost::optional<T>
MSalters

8

Tôi thấy cả hai mặt của tranh luận này và tôi nhận ra một số tiếng nói có ảnh hưởng khá lớn (ví dụ: Fowler) không ủng hộ việc trả lại null để giữ sạch mã, tránh các khối xử lý lỗi bổ sung, v.v.

Tuy nhiên, tôi có xu hướng đứng về phía những người đề xuất trả lại null. Tôi thấy có một sự khác biệt quan trọng trong việc gọi một phương thức và nó phản hồi với tôi không có bất kỳ dữ liệu nào và nó phản hồi với tôi có Chuỗi rỗng này .

Vì tôi đã thấy một số cuộc thảo luận tham khảo một lớp Người, hãy xem xét kịch bản mà bạn cố gắng tìm kiếm một thể hiện của lớp. Nếu bạn chuyển vào một số thuộc tính công cụ tìm (ví dụ: ID), khách hàng có thể ngay lập tức kiểm tra null để xem có tìm thấy giá trị nào không. Điều này không nhất thiết là ngoại lệ (do đó không cần ngoại lệ), nhưng nó cũng cần được ghi lại rõ ràng. Vâng, điều này đòi hỏi một số sự nghiêm khắc từ phía khách hàng, và không, tôi không nghĩ đó là một điều xấu cả.

Bây giờ hãy xem xét lựa chọn thay thế nơi bạn trả về một đối tượng Person hợp lệ ... không có gì trong đó. Bạn có đặt null trong tất cả các giá trị của nó (tên, địa chỉ, FavoritesDrink) hoặc bây giờ bạn có thể điền vào những giá trị đó với các đối tượng hợp lệ nhưng trống không? Làm thế nào để khách hàng của bạn bây giờ xác định rằng không có Người thực sự được tìm thấy? Họ có cần kiểm tra xem tên đó có phải là một chuỗi rỗng thay vì null không? Đây có phải là thứ thực sự sẽ dẫn đến sự lộn xộn và nhiều câu lệnh có nhiều mã hơn so với việc chúng ta chỉ kiểm tra null và tiếp tục không?

Một lần nữa, có những điểm ở hai bên của lập luận này tôi có thể đồng ý, nhưng tôi thấy điều này có ý nghĩa nhất đối với hầu hết mọi người (làm cho mã dễ duy trì hơn).


Sự khác biệt là, "có FindPersonhoặc GetPersontrả về null hoặc ném ngoại lệ nếu khóa không tồn tại?" Là người dùng API tôi không biết và tôi phải kiểm tra tài liệu. Mặt khác, bool TryGetPerson(Guid key, out Person person)không yêu cầu tôi kiểm tra tài liệu và tôi biết một ngoại lệ không bị ném vào số tiền cho một tình huống khá đặc biệt. Đó là điểm tôi đang cố gắng đưa ra trong câu trả lời của mình.
Scott Whitlock

4
Tôi nghĩ mọi người đã quá gắn bó với ngoại lệ. Ngoại lệ là xấu và tiêu thụ tài nguyên. Chưa kể mọi người làm thử và bắt các tuyên bố mà không giải quyết bất cứ điều gì. Các ngoại lệ là dấu hiệu rõ ràng rằng một cái gì đó đã đi sai về cơ bản. Chẳng hạn, các tham chiếu null đưa ra một ngoại lệ, vì rõ ràng sai đối với các đối tượng null. Thật sai lầm khi sử dụng nó để kiểm tra xem người đó có được tìm thấy hay không.
Vladimir Kocjancic

1
@VladimirKocjancic: Tôi hoàn toàn đồng ý: các trường hợp ngoại lệ nên được sử dụng cho tình huống đặc biệt, như lỗi phần mềm, lỗi phần cứng, cấu hình sai hệ thống. Nếu bạn đang tính toán một chức năng một phần (như findPerson) thì việc lấy lại không có kết quả là hoàn toàn bình thường. Điều này không nên dẫn đến một ngoại lệ.
Giorgio

6

Trả về null nếu bạn cần biết vật phẩm có tồn tại hay không. Nếu không, trả về kiểu dữ liệu dự kiến. Điều này đặc biệt đúng nếu bạn trả về một danh sách các mặt hàng. Thông thường sẽ an toàn nếu giả sử người gọi muốn có một danh sách, họ sẽ muốn lặp lại danh sách đó. Nhiều ngôn ngữ (hầu hết? Tất cả?) Không thành công nếu bạn cố gắng lặp lại null nhưng không phải khi bạn lặp lại một danh sách trống.

Tôi thấy khó chịu khi thử và sử dụng mã như thế này, chỉ để nó thất bại:

for thing in get_list_of_things() {
    do_something_clever(thing)
}

Tôi không nên thêm một trường hợp đặc biệt cho trường hợp khi không có điều gì .


7
Trong trường hợp danh sách hoặc nhóm giá trị khác được mong đợi, tôi luôn chọn trả về danh sách trống, vì lý do chính xác này. Hành động mặc định (lặp qua một danh sách trống) hầu như luôn luôn đúng và nếu không thì người gọi có thể kiểm tra rõ ràng để xem danh sách có trống không.
TMN

5

Nó phụ thuộc vào ngữ nghĩa của phương pháp. Bạn có thể chỉ định, phương thức của bạn chỉ chấp nhận "Không null". Trong Java, bạn có thể khai báo rằng sử dụng một số chú thích siêu dữ liệu:

string ReverseString(@NotNull String stringToReverse)

Bạn bằng cách nào đó nên xác định giá trị trả về. Nếu bạn khai báo, bạn chỉ chấp nhận các giá trị NotNull, bạn buộc phải trả về chuỗi trống (nếu đầu vào cũng là chuỗi trống).

Trường hợp thứ hai là một chút phức tạp trong ngữ nghĩa của nó. Nếu phương thức này là trả về một số người bằng khóa chính (và người đó dự kiến ​​sẽ tồn tại), cách tốt hơn là ném ngoại lệ.

Person FindPerson(int personID)

Nếu bạn tìm kiếm người theo một số id đoán (có vẻ như là số lẻ), bạn nên khai báo phương thức đó là @Nullable.


3

Tôi sẽ khuyên bạn nên sử dụng mẫu Null Object bất cứ khi nào có thể. Nó đơn giản hóa mã khi gọi các phương thức của bạn và bạn không được đọc mã xấu như

if (someObject != null && someObject.someMethod () != whateverValue)

Chẳng hạn, nếu một phương thức trả về một tập hợp các đối tượng phù hợp với một số mẫu, thì việc trả về một bộ sưu tập trống có ý nghĩa hơn là trả về nullvà việc lặp lại bộ sưu tập trống này sẽ hầu như không bị phạt hiệu năng. Một trường hợp khác sẽ là một phương thức trả về thể hiện của lớp được sử dụng để ghi dữ liệu, trả về một đối tượng Null thay vì nullthích hợp hơn theo quan điểm của tôi, vì nó không bắt buộc người dùng phải luôn kiểm tra xem tham chiếu được trả về có phải không null.

Trong trường hợp trả về nullcó ý nghĩa ( findPerson ()ví dụ gọi phương thức), tôi sẽ cố gắng cung cấp ít nhất một phương thức trả về nếu có đối tượng ( personExists (int personId)ví dụ), một ví dụ khác sẽ là containsKey ()phương thức trên MapJava). Nó làm cho mã của người gọi sạch hơn, vì bạn có thể dễ dàng thấy rằng có khả năng đối tượng mong muốn có thể không có sẵn (người không tồn tại, khóa không có trong bản đồ). Theo tôi, liên tục kiểm tra nếu một tài liệu tham khảo nulllàm xáo trộn mã theo ý kiến ​​của tôi.


3

null là điều tốt nhất để trả về khi và chỉ khi áp dụng các điều kiện sau:

  • kết quả null được mong đợi trong hoạt động bình thường . Có thể bạn sẽ không thể tìm thấy một người trong một số trường hợp hợp lý, vì vậy findPerson () trả về null là tốt. Tuy nhiên, nếu đó là một thất bại thực sự bất ngờ (ví dụ: trong một hàm gọi là saveMyImportantData ()) thì bạn nên ném Ngoại lệ. Có một gợi ý trong tên - Ngoại lệ dành cho trường hợp đặc biệt!
  • null được sử dụng để có nghĩa là "không tìm thấy / không có giá trị" . Nếu bạn có ý gì khác, sau đó trả lại một cái gì đó khác! Các ví dụ tốt sẽ là các phép toán dấu phẩy động trả về Infinity hoặc NaN - đây là các giá trị có ý nghĩa cụ thể nên sẽ rất khó hiểu nếu bạn trả về null ở đây.
  • Hàm này có nghĩa là trả về một giá trị đơn lẻ , chẳng hạn như findPerson (). Nếu nó được thiết kế để trả về một bộ sưu tập, ví dụ find findOldP People () thì một bộ sưu tập trống là tốt nhất. Một hệ quả của điều này là một hàm trả về một bộ sưu tập sẽ không bao giờ trả về null .

Ngoài ra, hãy chắc chắn rằng bạn ghi lại thực tế rằng hàm có thể trả về null.

Nếu bạn tuân theo các quy tắc này, nulls hầu như vô hại. Lưu ý rằng nếu bạn quên kiểm tra null, thông thường bạn sẽ nhận được NullPulumException ngay sau đó, đây thường là một lỗi khá dễ sửa. Cách tiếp cận nhanh không thành công này tốt hơn nhiều so với việc có giá trị trả về giả (ví dụ: chuỗi rỗng) được truyền âm thầm xung quanh hệ thống của bạn, có thể làm hỏng dữ liệu mà không đưa ra ngoại lệ.

Cuối cùng, nếu bạn áp dụng các quy tắc này cho hai chức năng được liệt kê trong câu hỏi:

  • FindPerson - chức năng này sẽ phù hợp để trả về null nếu không tìm thấy người đó
  • ReverseString - có vẻ như sẽ không bao giờ thất bại trong việc tìm kiếm kết quả nếu thông qua một chuỗi (vì tất cả các chuỗi có thể bị đảo ngược, bao gồm cả chuỗi trống). Vì vậy, nó sẽ không bao giờ trả về null. Nếu có gì đó không ổn (hết bộ nhớ?) Thì nó nên ném một ngoại lệ.

1

Theo tôi, có một sự khác biệt giữa trả về NULL, trả về một số kết quả trống (ví dụ: chuỗi rỗng hoặc danh sách trống) và ném ngoại lệ.

Tôi thường thực hiện theo cách tiếp cận sau đây. Tôi coi một hàm hoặc phương thức f (v1, ..., vn) gọi là ứng dụng của hàm

f : S x T1 x ... x Tn -> T

Trong đó S là "trạng thái của thế giới" T1, ..., Tn là các loại tham số đầu vào và T là loại trả về.

Trước tiên tôi cố gắng xác định chức năng này. Nếu hàm là một phần (tức là có một số giá trị đầu vào mà nó không được xác định) tôi trả về NULL để báo hiệu điều này. Điều này là do tôi muốn tính toán kết thúc bình thường và cho tôi biết rằng hàm tôi đã yêu cầu không được xác định trên các đầu vào đã cho. Việc sử dụng, ví dụ, một chuỗi rỗng làm giá trị trả về là không rõ ràng vì có thể hàm được xác định trên các đầu vào và chuỗi trống là kết quả chính xác.

Tôi nghĩ rằng việc kiểm tra thêm cho một con trỏ NULL trong mã gọi là cần thiết bởi vì bạn đang áp dụng một phần chức năng và đó là nhiệm vụ của phương thức được gọi để cho bạn biết nếu hàm nếu không được xác định cho đầu vào đã cho.

Tôi thích sử dụng các ngoại lệ cho các lỗi không cho phép thực hiện tính toán (nghĩa là không thể tìm thấy bất kỳ câu trả lời nào).

Ví dụ: giả sử tôi có một Khách hàng đẳng cấp và tôi muốn thực hiện một phương thức

Customer findCustomer(String customerCode)

để tìm kiếm một khách hàng trong cơ sở dữ liệu ứng dụng theo mã của nó. Trong phương pháp này, tôi sẽ

  • Trả về một đối tượng của lớp Khách hàng nếu truy vấn thành công,
  • Trả về null nếu truy vấn không tìm thấy bất kỳ khách hàng nào.
  • Ném một ngoại lệ nếu không thể kết nối với cơ sở dữ liệu.

Việc kiểm tra thêm cho null, vd

Customer customer = findCustomer("...");
if (customer != null && customer.getOrders() > 0)
{
    ...
}

là một phần của ngữ nghĩa của những gì tôi đang làm và tôi sẽ không chỉ "bỏ qua chúng" để làm cho mã được đọc tốt hơn. Tôi không nghĩ rằng đó là một cách thực hành tốt để đơn giản hóa ngữ nghĩa của vấn đề trong tay chỉ để đơn giản hóa mã.

Tất nhiên, vì việc kiểm tra null xảy ra rất thường xuyên sẽ tốt nếu ngôn ngữ hỗ trợ một số cú pháp đặc biệt cho nó.

Tôi cũng sẽ xem xét sử dụng mẫu Null Object (như được đề xuất bởi Laf) miễn là tôi có thể phân biệt đối tượng null của một lớp với tất cả các đối tượng khác.


0

Các phương thức trả về các bộ sưu tập sẽ trả về sản phẩm nào nhưng các phương thức khác có thể trả về null, vì đối với các bộ sưu tập, điều đó có thể xảy ra là bạn sẽ có đối tượng nhưng không có phần tử nào trong đó, vì vậy người gọi sẽ xác thực cho cả kích thước thay vì cả hai.


0

Đối với tôi có hai trường hợp ở đây. Nếu bạn đang trả lại một số loại danh sách, bạn sẽ luôn trả về danh sách cho dù thế nào đi chăng nữa, nếu bạn không có gì để đưa vào danh sách.

Trường hợp duy nhất mà tôi thấy bất kỳ cuộc tranh luận nào là khi bạn trả lại một mục duy nhất. Tôi thấy mình thích trả về null trong trường hợp thất bại trong tình huống như vậy trên cơ sở làm cho mọi thứ thất bại nhanh chóng.

Nếu bạn trả về một đối tượng null và người gọi cần một đối tượng thực sự, họ có thể tiếp tục và cố gắng sử dụng đối tượng null tạo ra hành vi không mong muốn. Tôi nghĩ sẽ tốt hơn cho thói quen bùng nổ nếu họ quên đối phó với tình huống này. Nếu có gì sai tôi muốn ngoại lệ càng sớm càng tốt. Bạn ít có khả năng gửi một lỗi theo cách này.


0

Thêm vào những gì mọi người đã nói về hàm tạo Maybekiểu:

Một lợi ích lớn khác, ngoài việc không mạo hiểm NPE, là một ngữ nghĩa. Trong thế giới chức năng, nơi chúng ta xử lý các hàm thuần túy, chúng ta muốn thử tạo các hàm mà khi được áp dụng chính xác tương đương với giá trị trả về của chúng . Hãy xem xét trường hợp String.reverse(): Đây là một hàm đưa ra một chuỗi nhất định đại diện cho phiên bản đảo ngược của chuỗi đó. Chúng tôi biết rằng chuỗi này tồn tại, bởi vì mọi chuỗi có thể được đảo ngược (chuỗi cơ bản là các bộ ký tự).

Bây giờ thì findCustomer(int id)sao? Hàm này đại diện cho khách hàng có ID đã cho. Đây là một cái gì đó có thể hoặc không tồn tại. Nhưng hãy nhớ rằng, các hàm có một giá trị trả về duy nhất, vì vậy bạn thực sự không thể nói "hàm này trả về một khách hàng với ID đã cho HOẶC nó trả về null.

Đây là vấn đề chính của tôi cả với việc trả lại nullvà trả về một đối tượng null. Họ đang gây hiểu lầm. nullkhông phải là khách hàng và nó cũng không thực sự là "sự vắng mặt của khách hàng". Đó là sự vắng mặt của BẤT CỨ NƠI NÀO. Nó chỉ là một con trỏ không chữ để KHÔNG. Mức độ rất thấp và rất xấu và rất dễ bị lỗi. Tôi nghĩ rằng một đối tượng null cũng khá sai lệch. Một khách hàng null là một khách hàng. Đó không phải là sự vắng mặt của một người, đó không phải là khách hàng có ID được yêu cầu, vì vậy việc trả lại nó chỉ đơn giản là SAI. Đây KHÔNG phải là khách hàng tôi yêu cầu, nhưng bạn vẫn khẳng định bạn đã trả lại một khách hàng. Nó có ID sai, rõ ràng đây là một lỗi. Nó phá vỡ hợp đồng được đề xuất bởi tên phương thức và chữ ký. Nó yêu cầu mã máy khách để thừa nhận những thứ về thiết kế của bạn hoặc để đọc tài liệu.

Cả hai vấn đề này đều được giải quyết bằng a Maybe Customer. Đột nhiên, chúng tôi không nói "chức năng này đại diện cho khách hàng với ID đã cho". Chúng tôi đang nói "chức năng này đại diện cho khách hàng với ID đã cho nếu nó tồn tại . Bây giờ rất rõ ràng và rõ ràng về những gì nó làm. Nếu bạn yêu cầu khách hàng có ID không tồn tại, bạn sẽ nhận được Nothing. Hơn null? Không chỉ đối với rủi ro NPE. Mà còn bởi vì loại Nothingkhông chỉ Maybe. Maybe CustomerNó có ý nghĩa ngữ nghĩa. Nó đặc biệt có nghĩa là "sự vắng mặt của một khách hàng", cho thấy rằng một khách hàng như vậy không tồn tại.

Một vấn đề khác với null tất nhiên là nó mơ hồ. Có phải là bạn đã không tìm thấy khách hàng, hoặc có lỗi kết nối db hay tôi chỉ không được phép gặp khách hàng đó?

Nếu bạn có một số trường hợp lỗi như thế này để xử lý, bạn có thể đưa ra ngoại lệ cho các phiên bản đó hoặc (và tôi thích cách này), bạn có thể trả lại Either CustomerLoadError Customer, đại diện cho một cái gì đó đã sai hoặc khách hàng bị trả lại. Nó có tất cả những lợi thế Maybe Customertrong khi cũng cho phép bạn chỉ định những gì có thể sai.

Điều chính tôi sau là nắm bắt hợp đồng của chức năng trong chữ ký của nó. Đừng giả sử mọi thứ, đừng dựa vào những quy ước thoáng qua, hãy nói rõ ràng.


0

1) Nếu ngữ nghĩa của hàm là nó có thể trả về không có gì, người gọi PHẢI kiểm tra cho nó, nếu không anh ta sẽ ví dụ. lấy tiền của bạn và đưa nó cho không ai.

2) Thật tốt khi thực hiện các chức năng mà về mặt ngữ nghĩa luôn trả lại một cái gì đó (hoặc ném).

Với một logger , sẽ rất hợp lý khi trả về logger không ghi nếu không có logger nào được xác định. Khi trả về bộ sưu tập, hầu như không bao giờ có ý nghĩa (ngoại trừ "mức đủ thấp", trong đó chính bộ sưu tập là dữ liệu, không phải là dữ liệu chứa), không trả về gì cả, vì một tập hợp trống là một tập hợp, không phải là không có gì.

Với một ví dụ cá nhân , tôi sẽ đi theo cách "ngủ đông" (get vs load, IIRC, nhưng ở đó, các đối tượng proxy phức tạp vì lười tải) có hai hàm, một hàm trả về null và thứ hai ném.


-1
  • Nên trả về NULL nếu ứng dụng mong muốn có dữ liệu nhưng dữ liệu không có sẵn. Ví dụ: dịch vụ trả về THÀNH PHỐ dựa trên mã zip sẽ trả về null nếu không tìm thấy thành phố. Người gọi sau đó có thể quyết định xử lý null hoặc nổ tung.

  • Danh sách trống nên được trả lại nếu có hai khả năng. Dữ liệu có sẵn hoặc KHÔNG có dữ liệu. Ví dụ: một dịch vụ trả về (các) THÀNH PHỐ nếu dân số lớn hơn số lượng nhất định. Nó có thể trả về một danh sách trống nếu Không có dữ liệu thỏa mãn các tiêu chí đã cho.


-2

Trở về NULL là một thiết kế khủng khiếp , trong thế giới hướng đối tượng. Tóm lại, việc sử dụng NULL dẫn đến:

  • xử lý lỗi đặc biệt (thay vì ngoại lệ)
  • ngữ nghĩa mơ hồ
  • chậm thay vì thất bại nhanh
  • tư duy máy tính so với tư duy đối tượng
  • đối tượng có thể thay đổi và không đầy đủ

Kiểm tra bài đăng trên blog này để được giải thích chi tiết: http://www.yegor256.com/2014/05/13/why-null-is-bad.html

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.