Là trả lại null thiết kế xấu? [đóng cửa]


127

Tôi đã nghe một số tiếng nói rằng việc kiểm tra giá trị null được trả về từ các phương thức là thiết kế xấu. Tôi muốn nghe một số lý do cho việc này.

mã giả:

variable x = object.method()
if (x is null) do something

13
Xây dựng: những người này nói nó xấu ở đâu? Liên kết?
jcollum

2
Nếu phương thức là thứ bạn có quyền kiểm soát, bạn có thể có các bài kiểm tra đơn vị để đảm bảo rằng nó không bao giờ trả về null, nếu không, tôi không hiểu tại sao nó sẽ là một thực tiễn tồi để kiểm tra xem nó có null không, sau cuộc gọi đó; nó có thể là một cách thực hành tồi đối với phương thức đó để trả về null, nhưng bạn phải bảo vệ mã của mình
BlackTigerX

9
Tăng ngoại lệ chỉ vì không có dữ liệu được trả lại là vô cùng khó chịu. Luồng chương trình bình thường không nên ném ngoại lệ.
Thorarin

4
@David: Đó là những gì tôi nói thực sự. Nếu một phương thức sẽ trả về dữ liệu, nhưng không có phương thức nào, điều đó có nghĩa là một cái gì đó cũng đã sai. Đó không phải là luồng chương trình bình thường :)
Thorarin

2
@Thorarin: Luồng chương trình "bình thường" là một khái niệm có thể kéo dài: không thực sự là cơ sở vững chắc cho một cuộc tranh luận.
Tomislav Nakic-Alfirevic

Câu trả lời:


206

Lý do đằng sau việc không trả về null là bạn không phải kiểm tra nó và do đó mã của bạn không cần phải đi theo một đường dẫn khác dựa trên giá trị trả về. Bạn có thể muốn kiểm tra Mẫu đối tượng Null cung cấp thêm thông tin về điều này.

Ví dụ, nếu tôi định nghĩa một phương thức trong Java trả về Bộ sưu tập, tôi thường muốn trả về một bộ sưu tập trống (nghĩa là Collections.emptyList()) thay vì null vì điều đó có nghĩa là mã máy khách của tôi sạch hơn; ví dụ

Collection<? extends Item> c = getItems(); // Will never return null.

for (Item item : c) { // Will not enter the loop if c is empty.
  // Process item.
}

... sạch hơn:

Collection<? extends Item> c = getItems(); // Could potentially return null.

// Two possible code paths now so harder to test.
if (c != null) {
  for (Item item : c) {
    // Process item.
  }
}

6
Có, cách tốt hơn là trả về null và hy vọng khách hàng nhớ xử lý vụ việc.
djna

10
Tôi sẽ vui vẻ đồng ý rằng trả về null là điên rồ khi nó được sử dụng thay thế cho các thùng chứa (hoặc chuỗi) trống. Đó không phải là trường hợp phổ biến mặc dù.
MSalters

2
+1 cho Mẫu đối tượng Null đối với tôi cũng vậy. Ngoài ra khi tôi thực sự muốn trả về null, tôi đã đặt tên phương thức như getCustomerOrNull () để làm cho nó rõ ràng. Tôi nghĩ rằng một phương pháp được đặt tên tốt khi người đọc không muốn xem xét việc thực hiện.
Mike Valenty

4
Nhận xét '// Hai đường dẫn mã có thể bây giờ' không chính xác; bạn có hai đường dẫn mã theo một trong hai cách. Tuy nhiên, với đối tượng Bộ sưu tập null trong ví dụ đầu tiên của bạn, đường dẫn mã 'null' ngắn hơn. Bạn vẫn có hai đường dẫn, và bạn vẫn cần kiểm tra hai đường dẫn.
Frerich Raabe

1
@MSalters - có thể cho rằng mọi biến trong các ngôn ngữ OO nulllà "thùng chứa", chứa 0 hoặc một con trỏ tới các đối tượng. (Đó chắc chắn là cách mà Haskell mô hình rõ ràng Maybetrong những trường hợp đó, và sẽ tốt hơn khi làm như vậy.)
Andrzej Doyle

71

Đây là lý do.

Trong Clean Code của Robert Martin , ông viết rằng trả về null là thiết kế tồi khi thay vào đó bạn có thể trả về mảng trống. Vì kết quả mong đợi là một mảng, tại sao không? Nó sẽ cho phép bạn lặp lại kết quả mà không cần thêm bất kỳ điều kiện nào. Nếu đó là số nguyên, có thể 0 sẽ đủ, nếu đó là hàm băm, hàm băm trống. Vân vân.

Tiền đề là không buộc mã gọi để xử lý ngay các vấn đề. Gọi mã có thể không muốn quan tâm đến họ. Đó cũng là lý do tại sao trong nhiều trường hợp ngoại lệ tốt hơn con số không.


2
Đây là Mẫu đối tượng Null được đề cập trong câu trả lời này: stackoverflow.com/questions/1274792/iêu
Scott Dorman

Ý tưởng ở đây là do lỗi, bạn trả về một phiên bản trống / trống của bất kỳ loại đối tượng nào bạn sẽ trả lại bình thường. Ví dụ, một mảng hoặc chuỗi trống sẽ hoạt động cho các trường hợp đó. Trả về "NULL" là phù hợp khi bạn thường trả về một con trỏ (vì về cơ bản NULL là một con trỏ trống). Trả về lỗi NULL từ một hàm thường trả về, giả sử, hàm băm có thể gây nhầm lẫn. Một số ngôn ngữ xử lý việc này tốt hơn những ngôn ngữ khác nhưng nói chung, tính nhất quán là cách thực hành tốt nhất.
bta

Bạn không nhất thiết phải trả về lỗi, trừ khi lỗi bằng cách nào đó kiểm soát luồng (không phải là thông lệ tốt) hoặc được trừu tượng hóa trong API hoặc giao diện. Lỗi ở đó để lan truyền đến bất kỳ cấp độ nào mà bạn quyết định bắt nó, do đó bạn không phải đối phó với nó trong bối cảnh gọi. Chúng theo mặc định null-object-mẫu-thân thiện.
Tối đa

Thật không may, mã gọi không phải luôn luôn xem xét trường hợp của một bộ sưu tập trống (giống như nó không xem xét trường hợp null). Điều đó có thể hoặc không thể là một vấn đề.
Sridhar Sarnobat

38

Sử dụng tốt để trả về null:

  • Nếu null là kết quả chức năng hợp lệ , ví dụ: FindFirstObjectThatNeedProcessing () có thể trả về null nếu không tìm thấy và người gọi nên kiểm tra tương ứng.

Sử dụng xấu: Cố gắng thay thế hoặc ẩn các tình huống đặc biệt như:

  • bắt (...) và trả về null
  • Khởi tạo phụ thuộc API không thành công
  • Hết dung lượng bộ nhớ trên ổ đĩa
  • Các tham số đầu vào không hợp lệ (lỗi lập trình, đầu vào phải được vệ sinh bởi người gọi)
  • Vân vân

Trong những trường hợp đó, ném một ngoại lệ là đầy đủ hơn kể từ:

  • Giá trị trả về null không cung cấp thông tin lỗi có ý nghĩa
  • Người gọi ngay lập tức rất có thể không thể xử lý tình trạng lỗi
  • Không có gì đảm bảo rằng người gọi đang kiểm tra kết quả null

Tuy nhiên, không nên sử dụng ngoại lệ để xử lý các điều kiện hoạt động bình thường của chương trình, như:

  • Tên người dùng / mật khẩu không hợp lệ (hoặc bất kỳ đầu vào nào do người dùng cung cấp)
  • Phá vỡ các vòng hoặc như gotos không địa phương

7
"Tên người dùng không hợp lệ" có vẻ như là một nguyên nhân tốt cho Ngoại lệ.
Thilo

11
Người dùng nhập thông tin đăng nhập / mật khẩu không hợp lệ không nên được coi là một điều kiện ngoại lệ, một phần của hoạt động chương trình bình thường. Tuy nhiên, hệ thống xác thực không phản hồi (ví dụ: thư mục hoạt động) là một điều kiện ngoại lệ.
ZeroConcept


2
Tôi muốn nói điều đó phụ thuộc: boolean login(String,String)có vẻ ổn và cũng vậyAuthenticationContext createAuthContext(String,String) throws AuthenticationException
sfussalanger

1
Trong ví dụ của bạn, nếu không có đối tượng đầu tiên cần xử lý, thì có hai cách hợp lý để xử lý trường hợp đó. Đầu tiên là mẫu Null Object. Tạo một lớp con cuối cùng đại diện cho một phiên bản không tồn tại của lớp. Nó có mặc định hợp lý và ném ngoại lệ khi các hành động vô nghĩa được yêu cầu. Kỹ thuật khác là Tùy chọn. Đây là những gì có sẵn trong Java8 và Scala. Một trong những điều này cho thấy ý định của các nhà phát triển. null không thể hiển thị ý định vì nó có quá nhiều ý nghĩa có thể. ** Hiển thị ý định ** là khía cạnh quan trọng nhất của mã.
scott m người làm vườn

29

Vâng, trả lại 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
  • suy nghĩ máy tính thay vì suy nghĩ đố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 . Thêm trong cuốn sách Đối tượng thanh lịch của tôi , Phần 4.1.


2
Tôi đồng ý mạnh mẽ. Tôi thậm chí không thích mẫu đối tượng null dường như là một chiến thuật trì hoãn 'minh oan'. Phương pháp của bạn là giả sử luôn luôn thành công hoặc có thể không. Nếu nó luôn luôn thành công, sau đó ném. Nếu nó có thể không thành công, sau đó thiết kế phương pháp để người tiêu dùng biết được, ví dụ như bool TryGetBlah(out blah)hay FirstOrNull()hay MatchOrFallback(T fallbackValue).
Luke Puplett

Tôi cũng không thích trả về null. Bạn đang tách nguyên nhân gốc từ triệu chứng, làm cho việc gỡ lỗi khó khăn hơn. Hoặc ném một ngoại lệ (thất bại nhanh) hoặc gọi một phương thức kiểm tra trả về boolean trước (ví dụ isEmpty()) và chỉ khi nó đúng thì gọi phương thức. Mọi người tranh luận với người thứ hai nói rằng đó là hiệu năng kém hơn - nhưng như Triết lý Unix nói, "giá trị thời gian của con người so với thời gian của máy" (nghĩa là hiệu năng chậm hơn đáng kể làm lãng phí ít thời gian hơn so với các nhà phát triển gỡ lỗi mã lỗi).
Sridhar Sarnobat

22

Ai nói đây là thiết kế xấu?

Kiểm tra null là một cách phổ biến, thậm chí được khuyến khích, nếu không, bạn sẽ gặp rủi ro về NullReferenceExceptions ở mọi nơi. Tốt hơn là xử lý lỗi một cách duyên dáng hơn là ném ngoại lệ khi bạn không cần.


11
+1. Mã ném ngoại lệ cho các vấn đề có thể được giảm nhẹ tại chỗ làm tôi buồn.
lừa

6
Bạn buồn đến mức nào khi các lập trình viên quên kiểm tra null và sau đó nhận được các ngoại lệ con trỏ null bí ẩn? Đã kiểm tra các trường hợp ngoại lệ, trong đó trình biên dịch nhắc nhở người dùng họ không xử lý các điều kiện lỗi tránh loại lỗi mã hóa.
djna

3
@djna: Tôi đoán điều này cũng đáng buồn nhưng tôi đã phát hiện ra rằng cùng một loại lập trình viên "quên" kiểm tra null là những người khi xử lý các trường hợp ngoại lệ được kiểm tra thường xuyên nuốt phải chúng.
Randy hỗ trợ Monica

5
Dễ dàng phát hiện sự hiện diện của khối bắt trống hơn là không có kiểm tra null.
Preston

20
@Preston: Tôi rất không đồng ý. Không có kiểm tra null bạn sẽ phát hiện ra ngay lập tức khi nó gặp sự cố. Các ngoại lệ bị nuốt chửng có thể gây ra các lỗi bí ẩn và tinh vi trong nhiều năm ...
Beska

18

Dựa trên những gì bạn đã nói cho đến nay, tôi nghĩ rằng không có đủ thông tin.

Trả về null từ phương thức CreatWidget () có vẻ xấu.

Trả về null từ phương thức FindFooInBar () có vẻ tốt.


4
Tương tự như quy ước của tôi: Truy vấn một mục - Create... trả về một thể hiện mới hoặc ném ; Get...trả về một thể hiện hiện tại dự kiến, hoặc ném ; GetOrCreate...trả về một thể hiện hiện có hoặc thể hiện mới nếu không tồn tại hoặc ném ; Find...trả về một thể hiện hiện có, nếu nó tồn tại, hoặcnull . Đối với các truy vấn bộ sưu tập - Get... luôn trả về một bộ sưu tập, trống nếu không tìm thấy mục phù hợp.
Johann Gerell

NULL xấu vì những lý do được đưa ra trong câu trả lời của yegor256 & hakuinin, trả lại một đối tượng hợp lệ nhưng trống rỗng đơn giản hóa lý do quá mức
Rob11311


10

Nó phụ thuộc vào ngôn ngữ bạn đang sử dụng. Nếu bạn đang sử dụng ngôn ngữ như C # trong đó cách thành ngữ biểu thị việc thiếu giá trị là trả về null, thì trả về null là một thiết kế tốt nếu bạn không có giá trị. Ngoài ra, trong các ngôn ngữ như Haskell sử dụng một cách đơn giản là Có thể đơn nguyên cho trường hợp này, sau đó trả về null sẽ là một thiết kế tồi (nếu nó thậm chí có thể).


2
+1 để đề cập có thể đơn nguyên. Tôi thấy định nghĩa nulltrong các ngôn ngữ như C # và Java thường bị quá tải và có một số ý nghĩa trong miền. Nếu bạn tra nulltừ khóa trong thông số kỹ thuật ngôn ngữ, nó chỉ có nghĩa là "một con trỏ không hợp lệ". Điều đó có thể có nghĩa là không có gì trong bất kỳ lĩnh vực vấn đề.
MattDavey

Vấn đề là bạn không biết đó là "giá trị còn thiếu" nullhay "không được khởi tạo"null
CervEd

5

Nếu bạn đọc tất cả các câu trả lời, nó trở nên rõ ràng câu trả lời cho câu hỏi này phụ thuộc vào loại phương pháp.

Thứ nhất, khi một cái gì đó đặc biệt xảy ra (IOpropet, v.v.), các ngoại lệ logic được đưa ra. Khi chính xác một cái gì đó đặc biệt có lẽ là một cái gì đó cho một chủ đề khác ..

Bất cứ khi nào một phương pháp được dự kiến ​​có thể không có kết quả, có hai loại:

  • Nếu có thể trả về giá trị trung tính, hãy làm như vậy .
    Các liệt kê trống, chuỗi vv là những ví dụ tốt
  • Nếu giá trị trung tính như vậy không tồn tại, null sẽ được trả về .
    Như đã đề cập, phương pháp được cho là có thể không có kết quả, vì vậy nó không phải là ngoại lệ, do đó không nên đưa ra một ngoại lệ. Không thể có giá trị trung tính (ví dụ: 0 không đặc biệt là kết quả trung tính, tùy thuộc vào chương trình)

Cho đến khi chúng ta có một cách chính thức để biểu thị rằng một hàm có thể hoặc không thể trả về null, tôi cố gắng có một quy ước đặt tên để biểu thị như vậy.
Giống như bạn có quy ước Thử một cái gì đó () cho các phương thức được dự kiến ​​sẽ thất bại, tôi thường đặt tên cho các phương thức của mình là Safe Something () khi phương thức trả về kết quả trung tính thay vì null.

Tôi vẫn chưa hoàn toàn ổn với cái tên này, nhưng không thể nghĩ ra thứ gì tốt hơn. Vì vậy, bây giờ tôi đang chạy với nó.


4

Tôi có một hội nghị trong lĩnh vực này phục vụ tôi tốt

Đối với các truy vấn mục đơn:

  • Create...trả về một thể hiện mới hoặc ném
  • Get...trả về một thể hiện hiện có hoặc ném
  • GetOrCreate...trả về một thể hiện hiện có hoặc thể hiện mới nếu không tồn tại hoặc ném
  • Find...trả về một thể hiện hiện có, nếu nó tồn tại, hoặcnull

Đối với các truy vấn bộ sưu tập:

  • Get... luôn trả về một bộ sưu tập, trống nếu không tìm thấy [1] mục phù hợp

[1] đưa ra một số tiêu chí, rõ ràng hoặc ẩn, được đưa ra trong tên hàm hoặc dưới dạng tham số.


Tại sao GetOne và FindOne trở lại khác nhau nếu không tìm thấy?
Vladimir Vukanac

Tôi đã mô tả sự khác biệt. Tôi sử dụng Getkhi tôi mong đợi nó ở đó, để nếu nó không ở đó, thì đó là lỗi và tôi ném - tôi không bao giờ cần phải kiểm tra giá trị trả về. Tôi sử dụng Findnếu tôi thực sự không biết nó có ở đó hay không - sau đó tôi cần kiểm tra giá trị trả lại.
Johann Gerell

3

Ngoại lệ là trường hợp ngoại lệ al.

Nếu chức năng của bạn dự định tìm một thuộc tính được liên kết với một đối tượng nhất định và đối tượng đó không có thuộc tính đó, thì có thể phù hợp để trả về null. Nếu đối tượng không tồn tại, ném ngoại lệ có thể phù hợp hơn. Nếu hàm có nghĩa là trả về một danh sách các thuộc tính và không có thuộc tính nào để trả về, thì việc trả về một danh sách trống có ý nghĩa - bạn đang trả về tất cả các thuộc tính bằng không.


1
Nếu bạn cố gắng sử dụng một thuộc tính không có giá trị, thì đó là một ngoại lệ. (Tôi giả sử thông số kỹ thuật của bạn nói rằng thuộc tính là tùy chọn.) Có một phương pháp riêng để kiểm tra xem giá trị của nó đã được đặt chưa.
Preston

3

Sẽ tốt hơn nếu bạn trả về null nếu làm như vậy có ý nghĩa theo một cách nào đó:

public String getEmployeeName(int id){ ..}

Trong trường hợp như thế này, việc trả về null là không hợp lý nếu id không tương ứng với thực thể hiện có, vì nó cho phép bạn phân biệt trường hợp không tìm thấy kết quả khớp với lỗi hợp pháp.

Mọi người có thể nghĩ rằng điều này là xấu vì nó có thể bị lạm dụng như một giá trị trả về "đặc biệt" biểu thị một điều kiện lỗi, điều này không tốt lắm, giống như trả lại mã lỗi từ một hàm nhưng khó hiểu vì người dùng phải kiểm tra trả về null, thay vì bắt các ngoại lệ thích hợp, vd

public Integer getId(...){
   try{ ... ; return id; }
   catch(Exception e){ return null;}
}

3
Vâng Nếu bạn có một điều kiện lỗi, ném một ngoại lệ.
David Thornley

3
Đây là một trường hợp ngoại lệ sẽ được bảo hành. Nếu bạn đang yêu cầu tên của một nhân viên không tồn tại, rõ ràng có điều gì đó không ổn.
Thorarin

Tôi nghĩ rằng đó là một chút theo nghĩa đen, Thorarin - chắc chắn bạn có thể ngụy biện với ví dụ của tôi, nhưng tôi chắc chắn bạn có thể tưởng tượng một số trường hợp của hàm trả về giá trị khớp hoặc null nếu không có kết quả khớp. Làm thế nào về việc nhận được một giá trị từ một khóa trong một hashtable? (nên nghĩ về ví dụ đó ở nơi đầu tiên).
Steve B.

1
Một bảng băm trả về null cho một khóa không tồn tại là xấu. Làm như vậy tự động có nghĩa là bạn không thể lưu trữ null dưới dạng giá trị mà không có mã không nhất quán.
RHSeeger

3

Nó không nhất thiết là một thiết kế tồi - như với rất nhiều quyết định thiết kế, nó phụ thuộc.

Nếu kết quả của phương thức là một cái gì đó sẽ không có kết quả tốt trong sử dụng bình thường, trả về null là tốt:

object x = GetObjectFromCache();   // return null if it's not in the cache

Nếu thực sự phải luôn luôn có một kết quả khác không, thì có lẽ tốt hơn là ném một ngoại lệ:

try {
   Controller c = GetController();    // the controller object is central to 
                                      //   the application. If we don't get one, 
                                      //   we're fubar

   // it's likely that it's OK to not have the try/catch since you won't 
   // be able to really handle the problem here
}
catch /* ... */ {
}

2
Mặc dù bạn có thể đăng nhập một lỗi chẩn đoán ngay tại đó, sau đó có thể lấy lại ngoại lệ. Thỉnh thoảng thu thập dữ liệu thất bại đầu tiên có thể rất hữu ích.
djna

Hoặc bọc ngoại lệ trong RuntimeException với thông tin chẩn đoán trong chuỗi thông báo. Giữ thông tin cùng nhau.
Thorbjørn Ravn Andersen

Bây giờ (năm 2018) tôi không còn có thể đồng ý và trong những trường hợp như vậy, chúng tôi nên ưu tiên quay lạiOptional<>
Piotr Müller

2

Đối với các kịch bản nhất định, bạn muốn nhận thấy một thất bại ngay khi nó xảy ra.

Kiểm tra chống lại NULL và không xác nhận (đối với lỗi lập trình viên) hoặc ném (đối với lỗi người dùng hoặc người gọi) trong trường hợp lỗi có thể có nghĩa là các sự cố sau này khó theo dõi hơn, vì không tìm thấy trường hợp lẻ ban đầu.

Hơn nữa, bỏ qua lỗi có thể dẫn đến khai thác bảo mật. Có lẽ sự vô hiệu đến từ thực tế là một bộ đệm đã bị ghi đè hoặc tương tự. Bây giờ, bạn không gặp sự cố, điều đó có nghĩa là người khai thác có cơ hội thực thi mã của bạn.


2

Những lựa chọn thay thế nào bạn thấy để trở về null?

Tôi thấy hai trường hợp:

  • findAnItem (id). Điều này nên làm gì nếu không tìm thấy vật phẩm

Trong trường hợp này, chúng tôi có thể: Trả về Null hoặc ném ngoại lệ (đã chọn) (hoặc có thể tạo một mục và trả lại)

  • listItemsMatching (tiêu chí) nên trả lại cái gì nếu không tìm thấy gì?

Trong trường hợp này, chúng tôi có thể trả về Null, trả về một danh sách trống hoặc ném Ngoại lệ.

Tôi tin rằng return null có thể kém hơn các lựa chọn thay thế vì nó yêu cầu khách hàng nhớ kiểm tra null, lập trình viên quên và viết mã

x = find();
x.getField();  // bang null pointer exception

Trong Java, ném một ngoại lệ được kiểm tra, RecordNotFoundException, cho phép trình biên dịch nhắc nhở khách hàng xử lý trường hợp.

Tôi thấy rằng các tìm kiếm trả về danh sách trống có thể khá thuận tiện - chỉ cần điền vào màn hình với tất cả nội dung của danh sách, ồ nó trống, mã "chỉ hoạt động".


3
Việc đưa ra các ngoại lệ để chỉ ra một điều kiện dự kiến ​​có thể xảy ra trong luồng thông thường của chương trình dẫn đến mã thực sự xấu xí, thuộc loại thử {x = find ()} Catch (RecordNotFound e) {// do Stuff}.
quant_dev

Trả về danh sách trống là một giải pháp tốt nhưng chỉ khi phương thức nằm trong ngữ cảnh có thể trả về danh sách. Đối với các tình huống "findById", bạn cần trả về null. Tôi không thích ngoại lệ RecordNotFound.
Thilo

Vì vậy, đó là mấu chốt của sự khác biệt trong quan điểm của chúng tôi. Trước mắt tôi không có nhiều khác biệt về vẻ đẹp giữa x = find (); if (x = null) {work} other {do Stuff} và thử bắt. Và, nếu cần tôi sẵn sàng hy sinh cái đẹp cho sự đúng đắn của mã. Trong cuộc sống của tôi, tôi thường gặp mã nơi các giá trị trả về chưa được kiểm tra.
djna

1

Đôi khi, trả về NULL là điều nên làm, nhưng cụ thể là khi bạn xử lý các chuỗi khác nhau (mảng, danh sách, chuỗi, những gì bạn có), có lẽ tốt hơn là trả về chuỗi có độ dài bằng không, vì nó dẫn đến mã ngắn hơn và hy vọng mã dễ hiểu hơn, trong khi không cần viết nhiều hơn về phần của người triển khai API.



1

Ý tưởng cơ bản đằng sau chủ đề này là lập trình phòng thủ. Đó là, mã chống lại sự bất ngờ. Có một loạt các câu trả lời khác nhau:

Adamski đề nghị xem xét Mô hình đối tượng Null, với câu trả lời được bình chọn cho đề xuất đó.

Michael Valenty cũng đề xuất một quy ước đặt tên để nói với nhà phát triển những gì có thể được mong đợi. ZeroConcept đề xuất sử dụng Exception đúng cách, nếu đó là lý do cho NULL. Và những người khác.

Nếu chúng ta đưa ra "quy tắc" mà chúng ta luôn muốn thực hiện lập trình phòng thủ thì chúng ta có thể thấy rằng những đề xuất này là hợp lệ.

Nhưng chúng tôi có 2 kịch bản phát triển.

Các lớp "tác giả" của một nhà phát triển: Tác giả

Các lớp "được tiêu thụ" bởi nhà phát triển (có thể) khác: Nhà phát triển

Bất kể một lớp có trả về NULL cho các phương thức có giá trị trả về hay không, Nhà phát triển sẽ cần kiểm tra xem đối tượng có hợp lệ hay không.

Nếu nhà phát triển không thể làm điều này, thì lớp / phương thức đó không mang tính quyết định. Đó là, nếu "phương thức gọi" để lấy đối tượng không thực hiện những gì nó "quảng cáo" (ví dụ getEmployee) thì nó đã phá vỡ hợp đồng.

Là một tác giả của một lớp, tôi luôn muốn trở nên tử tế và phòng thủ (và có tính quyết định) khi tạo ra một phương thức.

Vì vậy, nếu NULL hoặc NULL OBOG (ví dụ: nếu (nhân viên là NullEmployee.ISVALID)) cần được kiểm tra và điều đó có thể cần phải xảy ra với một bộ sưu tập Nhân viên, thì cách tiếp cận đối tượng null là cách tiếp cận tốt hơn.

Nhưng tôi cũng thích đề xuất của Michael Valenty về cách đặt tên phương thức PHẢI trả về null, ví dụ getEmployeeOrNull.

Một Tác giả đưa ra một ngoại lệ sẽ loại bỏ sự lựa chọn cho nhà phát triển để kiểm tra tính hợp lệ của đối tượng, điều này rất tệ đối với một bộ sưu tập các đối tượng và buộc nhà phát triển phải xử lý ngoại lệ khi phân nhánh mã tiêu thụ của họ.

Là một nhà phát triển tiêu thụ lớp, tôi hy vọng tác giả cho tôi khả năng tránh hoặc lập trình cho tình huống null mà lớp / phương thức của họ có thể phải đối mặt.

Vì vậy, là một nhà phát triển, tôi sẽ lập trình phòng thủ chống lại NULL từ một phương thức. Nếu tác giả đã đưa cho tôi một hợp đồng luôn trả về một đối tượng (ĐỐI TƯỢNG NULL luôn luôn làm như vậy) và đối tượng đó có một phương thức / thuộc tính để kiểm tra tính hợp lệ của đối tượng, thì tôi sẽ sử dụng phương thức / thuộc tính đó để tiếp tục sử dụng đối tượng , khác đối tượng không hợp lệ và tôi không thể sử dụng nó.

Điểm mấu chốt là Tác giả của Lớp / Phương thức phải cung cấp các cơ chế mà Nhà phát triển có thể sử dụng trong lập trình phòng thủ của họ. Đó là, một ý định rõ ràng hơn của phương pháp.

Nhà phát triển phải luôn sử dụng lập trình phòng thủ để kiểm tra tính hợp lệ của các đối tượng được trả về từ một lớp / phương thức khác.

Trân trọng

GregJF


0

Các tùy chọn khác cho điều này là: trả về một số giá trị biểu thị thành công hay không (hoặc loại lỗi), nhưng nếu bạn chỉ cần giá trị boolean sẽ biểu thị thành công / thất bại, trả về null cho thất bại và một đối tượng cho thành công sẽ không ít chính xác hơn, sau đó trả về true / false và nhận đối tượng thông qua tham số.
Cách tiếp cận khác sẽ sử dụng ngoại lệ để chỉ ra những thất bại, nhưng ở đây - thực sự có nhiều tiếng nói hơn, nói rằng đây là cách thực hành BAD (vì sử dụng ngoại lệ có thể thuận tiện nhưng có nhiều nhược điểm).
Vì vậy, cá nhân tôi không thấy có gì xấu khi trả về null vì dấu hiệu cho thấy có gì đó không ổn và kiểm tra lại sau (để thực sự biết bạn đã thành công hay chưa). Ngoài ra, mù quáng nghĩ rằng phương thức của bạn sẽ không trả về NULL, và sau đó dựa vào mã của bạn, có thể dẫn đến các lỗi khác, đôi khi khó tìm (mặc dù trong hầu hết các trường hợp, nó sẽ làm sập hệ thống của bạn :), vì bạn sẽ tham khảo 0x00000000 sớm hay muộn).


0

Các hàm null ngoài ý muốn có thể phát sinh trong quá trình phát triển một chương trình phức tạp và giống như mã chết, các sự cố như vậy chỉ ra các lỗ hổng nghiêm trọng trong cấu trúc chương trình.

Hàm hoặc phương thức null thường được sử dụng làm hành vi mặc định của hàm có thể phục hồi hoặc phương thức có thể ghi đè trong khung đối tượng.

Null_feft @wikipedia


0

Chà, nó chắc chắn phụ thuộc vào mục đích của phương pháp ... Đôi khi, một lựa chọn tốt hơn sẽ là ném một ngoại lệ. Tất cả phụ thuộc vào từng trường hợp.


0

Nếu mã là một cái gì đó như:

command = get_something_to_do()

if command:  # if not Null
    command.execute()

Nếu bạn có một đối tượng giả mà phương thức exec () không làm gì và bạn trả về nó thay vì Null trong các trường hợp thích hợp, bạn không phải kiểm tra trường hợp Null và thay vào đó chỉ có thể làm:

get_something_to_do().execute()

Vì vậy, ở đây, vấn đề không nằm ở việc kiểm tra NULL với một ngoại lệ, mà thay vào đó là giữa người gọi phải xử lý các trường hợp không đặc biệt khác nhau (theo bất kỳ cách nào) hay không.


0

Đối với trường hợp sử dụng của tôi, tôi cần trả về Bản đồ từ phương thức và sau đó tìm kiếm một khóa cụ thể. Nhưng nếu tôi trả về một Bản đồ trống, thì nó sẽ dẫn đến NullPulumException và sau đó nó sẽ không khác nhiều khi trả về null thay vì một Bản đồ trống. Nhưng từ Java8 trở đi, chúng tôi có thể sử dụng Tùy chọn . Trên đây là lý do rất nhiều khái niệm tùy chọn đã được giới thiệu.


-3

Ngày mai

Trả lại NULL khi bạn không thể tạo đối tượng mới là cách thực hành chuẩn cho nhiều API.

Tại sao cái thiết kế tồi tệ đó tôi không biết.

Chỉnh sửa: Điều này đúng với các ngôn ngữ mà bạn không có ngoại lệ, chẳng hạn như C, nơi nó đã là quy ước trong nhiều năm.

HTH

'Hạnh phúc,


2
Nếu bạn không thể tạo một đối tượng, bạn thực sự nên ném Ngoại lệ. Nếu bạn không thể tìm thấy một đối tượng phù hợp với một số truy vấn, thì trả về null là cách để đi.
Thilo

@Thilo, chỉ cho tôi cách làm điều đó trong C và tôi sẽ quan tâm nhất.
Rob Wells

@RobWells C không phải là ngôn ngữ hướng đối tượng, nhưng câu hỏi này được gắn thẻ là "oop"
yegor256

@ yegor256 Bạn nói đúng. Và tôi đã bỏ lỡ thẻ OOP ban đầu. Nhưng, như BS nói, một lớp trong C ++ chỉ là một cấu trúc với một số chức năng thành viên bổ sung được thêm vào và một số quản lý bộ nhớ ưa thích xảy ra trong nội bộ. Nhưng nếu một API bị hủy bỏ để trả về một cấu trúc, thì việc trả về NUL khi bạn không thể tạo cấu trúc thường là quy ước.
Rob Wells
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.