Tôi có một phương thức được cho là trả về một đối tượng nếu nó được tìm thấy.
Nếu nó không được tìm thấy, tôi nên:
- trả về null
- ném một ngoại lệ
- khác
Tôi có một phương thức được cho là trả về một đối tượng nếu nó được tìm thấy.
Nếu nó không được tìm thấy, tôi nên:
Câu trả lời:
Nếu bạn luôn mong muốn tìm thấy một giá trị thì hãy ném ngoại lệ nếu nó bị thiếu. Ngoại lệ có nghĩa là có một vấn đề.
Nếu giá trị có thể bị thiếu hoặc hiện tại và cả hai đều hợp lệ cho logic ứng dụng thì trả về null.
Quan trọng hơn: Bạn làm gì ở những nơi khác trong mã? Tính nhất quán là quan trọng.
GetPersonById(25)
sẽ ném nếu người đó đã bị xóa, nhưng GetPeopleByHairColor("red")
sẽ trả về một kết quả trống. Vì vậy, tôi nghĩ rằng các thông số nói lên điều gì đó về sự mong đợi.
Chỉ ném một ngoại lệ nếu nó thực sự là một lỗi. Nếu đó là hành vi dự kiến cho đối tượng không tồn tại, trả về null.
Nếu không thì đó là vấn đề ưu tiên.
Như một quy tắc chung, nếu phương thức luôn luôn trả về một đối tượng, thì đi với ngoại lệ. Nếu bạn dự đoán null thường xuyên và muốn xử lý nó theo một cách nhất định, hãy đi với null.
Dù bạn làm gì, tôi khuyên bạn nên chống lại tùy chọn thứ ba: Trả lại một chuỗi có nội dung "WTF".
Nếu null không bao giờ chỉ ra lỗi thì chỉ cần trả về null.
Nếu null luôn là một lỗi thì ném ngoại lệ.
Nếu null đôi khi là một ngoại lệ thì mã hai thói quen. Một thường trình đưa ra một ngoại lệ và cái còn lại là một thói quen kiểm tra boolean trả về đối tượng trong một tham số đầu ra và thường trình trả về sai nếu không tìm thấy đối tượng.
Thật khó để sử dụng sai thói quen Thử. Thật dễ dàng để quên kiểm tra null.
Vì vậy, khi null là một lỗi bạn chỉ cần viết
object o = FindObject();
Khi null không phải là lỗi, bạn có thể mã hóa cái gì đó như
if (TryFindObject(out object o)
// Do something with o
else
// o was not found
find
và findOrFail
từ Laravel Eloquent
TryFindObject
phương thức không? Tuples dường như là một mô hình lười biếng cho các lập trình viên, những người không muốn dành thời gian để xác định một đối tượng gói gọn nhiều giá trị. Đó là về cơ bản tất cả các bộ dữ liệu là cốt lõi.
Tôi chỉ muốn tóm tắt lại các tùy chọn được đề cập trước đó, ném một số tùy chọn mới vào:
Hoặc bạn có thể kết hợp các tùy chọn sau:
Cung cấp một số phiên bản quá tải của getter của bạn, để người gọi có thể quyết định đường nào để đi. Trong hầu hết các trường hợp, chỉ có cái đầu tiên có triển khai thuật toán tìm kiếm và những cái khác chỉ bao quanh cái đầu tiên:
Object findObjectOrNull(String key);
Object findObjectOrThrow(String key) throws SomeException;
Object findObjectOrCreate(String key, SomeClass dataNeededToCreateNewObject);
Object findObjectOrDefault(String key, Object defaultReturnValue);
Ngay cả khi bạn chọn chỉ cung cấp một triển khai, bạn có thể muốn sử dụng quy ước đặt tên như thế để làm rõ hợp đồng của mình và điều đó cũng giúp bạn nên quyết định thêm các triển khai khác.
Bạn không nên quá lạm dụng nó, nhưng nó có thể hữu ích, đặc biệt khi viết Lớp trợ giúp mà bạn sẽ sử dụng trong hàng trăm ứng dụng khác nhau với nhiều quy ước xử lý lỗi khác nhau.
Expected<T> findObject(String)
nơi Expected<T>
có các chức năng orNull()
, orThrow()
, orSupplied(Supplier<T> supplier)
, orDefault(T default)
. Điều này trừu tượng hóa việc nhận dữ liệu từ việc xử lý lỗi
Sử dụng mẫu đối tượng null hoặc ném ngoại lệ.
Person somePerson = personRepository.find("does-not-exist");
giả sử phương thức này trả về một đối tượng null cho ID does-not-exist
. Điều gì sau đó sẽ là hành vi chính xác cho somePerson.getAge()
? Ngay bây giờ, tôi chưa tin rằng mẫu đối tượng null là giải pháp phù hợp cho việc tra cứu thực thể.
Ưu điểm của việc ném ngoại lệ:
Để biết thêm giải thích với các ví dụ, xem: http://metatations.com/2011/11/17/retinating-null-vs-throwing-an-exception/
nó phụ thuộc nếu ngôn ngữ và mã của bạn quảng bá: LBYL (xem trước khi bạn nhảy) hoặc EAFP (dễ xin tha thứ hơn là cho phép)
LBYL nói rằng bạn nên kiểm tra các giá trị (vì vậy trả về null)
EAFP nói chỉ cần thử thao tác và xem nếu nó không thành công (ném ngoại lệ)
mặc dù tôi đồng ý với ở trên .. ngoại lệ nên được sử dụng cho các điều kiện ngoại lệ / lỗi và trả về null là tốt nhất khi sử dụng séc.
EAFP so với LBYL trong Python:
http://mail.python.org/pipermail/python-list/2003-May/205182.html
( Lưu trữ web )
Chỉ cần tự hỏi: "đó có phải là một trường hợp đặc biệt mà đối tượng không được tìm thấy" không? Nếu điều đó được dự kiến sẽ xảy ra trong quá trình bình thường của chương trình của bạn, có lẽ bạn không nên đưa ra một ngoại lệ (vì đó không phải là hành vi đặc biệt).
Phiên bản ngắn: sử dụng ngoại lệ để xử lý hành vi đặc biệt, không xử lý luồng kiểm soát thông thường trong chương trình của bạn.
-Alan.
Các ngoại lệ có liên quan đến Thiết kế theo Hợp đồng.
Giao diện của một đối tượng thực sự là một hợp đồng giữa hai đối tượng, người gọi phải đáp ứng hợp đồng nếu không người nhận có thể thất bại với một ngoại lệ. Có hai hợp đồng có thể
1) tất cả đầu vào phương thức là hợp lệ, trong trường hợp đó bạn phải trả về null khi không tìm thấy đối tượng.
2) chỉ một số đầu vào là hợp lệ, nghĩa là kết quả trong một đối tượng tìm thấy. Trong trường hợp đó, bạn PHẢI đưa ra một phương thức thứ hai cho phép người gọi xác định xem liệu đầu vào của nó có đúng không. Ví dụ
is_present(key)
find(key) throws Exception
NẾU và CHỈ NẾU bạn cung cấp cả hai phương thức của hợp đồng thứ 2, bạn được phép ném ngoại lệ là không tìm thấy gì!
Phụ thuộc vào những gì nó có nghĩa là không tìm thấy đối tượng.
Nếu đó là một trạng thái bình thường, sau đó trả về null. Đây chỉ là một cái gì đó có thể xảy ra một lần trong một thời gian, và người gọi nên kiểm tra nó.
Nếu đó là một lỗi, sau đó đưa ra một ngoại lệ, người gọi sẽ quyết định phải làm gì với tình trạng lỗi của đối tượng bị thiếu.
Cuối cùng, một trong hai sẽ hoạt động, mặc dù hầu hết mọi người thường coi đó là cách thực hành tốt để chỉ sử dụng Ngoại lệ khi một cái gì đó, tốt, ngoại lệ đã xảy ra.
Dưới đây là một vài gợi ý.
Nếu trả về một bộ sưu tập, tránh trả về null, hãy trả lại một bộ sưu tập trống, giúp việc liệt kê dễ dàng hơn để xử lý mà không cần kiểm tra null trước.
Một số API .NET sử dụng mẫu tham số throwOnError, cho phép người gọi lựa chọn xem đó có thực sự là một tình huống đặc biệt hay không nếu không tìm thấy đối tượng. Type.GetType là một ví dụ về điều này. Một mẫu phổ biến khác với BCL là mẫu TryGet trong đó boolean được trả về và giá trị được truyền qua một tham số đầu ra.
Bạn cũng có thể xem xét mẫu Null Object trong một số trường hợp có thể là mặc định hoặc phiên bản không có hành vi. Điều quan trọng là tránh kiểm tra null trong toàn bộ cơ sở mã. Xem tại đây để biết thêm thông tin http://geekswithbloss.net/dseller/archive/2006/09/08/90656.aspx
Trả về null thay vì ném một ngoại lệ và ghi lại rõ ràng khả năng giá trị trả về null trong tài liệu API. Nếu mã gọi không tôn trọng API và kiểm tra trường hợp null, rất có thể nó sẽ dẫn đến một loại "ngoại lệ con trỏ null" nào đó :)
Trong C ++, tôi có thể nghĩ ra 3 hương vị khác nhau khi thiết lập một phương thức tìm thấy một đối tượng.
Lựa chọn A
Object *findObject(Key &key);
Trả về null khi không thể tìm thấy đối tượng. Đẹp và đơn giản. Tôi sẽ đi với cái này Các phương pháp thay thế dưới đây dành cho những người không ghét các nhân viên khác.
Lựa chọn B
void findObject(Key &key, Object &found);
Truyền tham chiếu đến biến sẽ nhận đối tượng. Phương thức đưa ra một ngoại lệ khi không thể tìm thấy một đối tượng. Quy ước này có lẽ phù hợp hơn nếu nó không thực sự được mong đợi cho một đối tượng không được tìm thấy - do đó bạn đưa ra một ngoại lệ để biểu thị rằng đó là một trường hợp bất ngờ.
Tùy chọn C
bool findObject(Key &key, Object &found);
Phương thức trả về false khi không thể tìm thấy một đối tượng. Ưu điểm của tùy chọn A này là bạn có thể kiểm tra trường hợp lỗi trong một bước rõ ràng:
if (!findObject(myKey, myObj)) { ...
Chỉ đề cập đến trường hợp null không được coi là một hành vi đặc biệt, tôi chắc chắn cho phương pháp thử, rõ ràng, không cần phải "đọc sách" hoặc "nhìn trước khi bạn nhảy" như đã nói ở đây
nên về cơ bản:
bool TryFindObject(RequestParam request, out ResponseParam response)
và điều này có nghĩa là mã của người dùng cũng sẽ rõ ràng
...
if(TryFindObject(request, out response)
{
handleSuccess(response)
}
else
{
handleFailure()
}
...
Nói chung nó sẽ trả về null. Mã gọi phương thức sẽ quyết định nên ném ngoại lệ hay thử thứ khác.
Hoặc trả lại Tùy chọn
Một tùy chọn về cơ bản là một lớp container buộc khách hàng xử lý các trường hợp gian hàng. Scala có khái niệm này, tra cứu API của nó.
Sau đó, bạn có các phương thức như T getOrElse (T value IfNull) trên đối tượng này, hoặc trả về đối tượng tìm thấy hoặc một đặc tả của máy khách.
Thật không may, JDK không nhất quán, nếu bạn cố truy cập khóa không tồn tại trong gói tài nguyên, bạn sẽ không tìm thấy ngoại lệ và khi bạn yêu cầu giá trị từ bản đồ, bạn sẽ nhận được null nếu nó không tồn tại. Vì vậy, tôi sẽ thay đổi câu trả lời của người chiến thắng thành như sau, nếu giá trị tìm thấy có thể là null, sau đó đưa ra ngoại lệ khi không tìm thấy, nếu không trả về null. Vì vậy, hãy tuân theo quy tắc với một ngoại lệ, nếu bạn cần biết tại sao giá trị không được tìm thấy thì luôn tăng ngoại lệ hoặc ..
Miễn là nó phải trả lại một tham chiếu đến đối tượng, trả về NULL là tốt.
Tuy nhiên, nếu nó trả lại toàn bộ điều đẫm máu (như trong C ++ nếu bạn làm: 'return blah;' thay vì 'return & blah;' (hoặc 'blah' là một con trỏ), thì bạn không thể trả lại NULL, vì đó là không thuộc loại 'đối tượng'. Trong trường hợp đó, ném một ngoại lệ hoặc trả lại một đối tượng trống không có cờ thành công là cách tôi sẽ tiếp cận vấn đề.
Đừng nghĩ rằng bất cứ ai đề cập đến chi phí xử lý ngoại lệ - cần có thêm tài nguyên để tải lên và xử lý ngoại lệ, trừ khi đó là sự kiện giết chết ứng dụng thực sự hoặc quá trình dừng (tiến lên sẽ gây hại nhiều hơn lợi) Tôi sẽ chọn trả lại giá trị môi trường gọi có thể giải thích khi nó thấy phù hợp.
Tôi đồng ý với những gì có vẻ là sự đồng thuận ở đây (trả về null nếu "không tìm thấy" là kết quả bình thường có thể xảy ra hoặc ném ngoại lệ nếu ngữ nghĩa của tình huống yêu cầu luôn tìm thấy đối tượng).
Tuy nhiên, có khả năng thứ ba có thể có ý nghĩa tùy thuộc vào tình huống cụ thể của bạn. Phương thức của bạn có thể trả về một đối tượng mặc định thuộc loại nào đó trong điều kiện "không tìm thấy", cho phép mã cuộc gọi được đảm bảo rằng nó sẽ luôn nhận được một đối tượng hợp lệ mà không cần kiểm tra null hoặc bắt ngoại lệ.
Trường hợp ngoại lệ nên được đặc biệt . Trả về null nếu nó hợp lệ để trả về null .
Nếu phương thức trả về một bộ sưu tập, thì trả về một bộ sưu tập trống (như đã nói ở trên). Nhưng xin vui lòng không Bộ sưu tập.EMPTY_LIST hoặc như vậy! (trong trường hợp Java)
Nếu phương thức lấy lại một đối tượng, thì Bạn có một số tùy chọn.
Hãy cẩn thận, nếu bạn quyết định trả về null. Nếu bạn không phải là lập trình viên duy nhất trong dự án, bạn sẽ nhận được NullPulumExceptions (bằng Java hoặc bất kỳ ngôn ngữ nào khác) trong thời gian chạy! Vì vậy, đừng trả về null mà không được kiểm tra tại thời điểm biên dịch.
null
. Xem câu trả lời được bình chọn hàng đầu để biết thêm.
Nếu bạn đang sử dụng một thư viện này hay cách khác lớp mà ném một ngoại lệ, bạn nên rethrow nó. Đây là một ví dụ. Example2.java giống như thư viện và example.java sử dụng đối tượng của nó. Main.java là một ví dụ để xử lý Ngoại lệ này. Bạn sẽ hiển thị một thông điệp có ý nghĩa và (nếu cần) ngăn xếp theo dõi cho người dùng ở phía gọi.
Main.java
public class Main {
public static void main(String[] args) {
Example example = new Example();
try {
Example2 obj = example.doExample();
if(obj == null){
System.out.println("Hey object is null!");
}
} catch (Exception e) {
System.out.println("Congratulations, you caught the exception!");
System.out.println("Here is stack trace:");
e.printStackTrace();
}
}
}
Ví dụ
/**
* Example.java
* @author Seval
* @date 10/22/2014
*/
public class Example {
/**
* Returns Example2 object
* If there is no Example2 object, throws exception
*
* @return obj Example2
* @throws Exception
*/
public Example2 doExample() throws Exception {
try {
// Get the object
Example2 obj = new Example2();
return obj;
} catch (Exception e) {
// Log the exception and rethrow
// Log.logException(e);
throw e;
}
}
}
Ví dụ2.java
/**
* Example2.java
* @author Seval
*
*/
public class Example2 {
/**
* Constructor of Example2
* @throws Exception
*/
public Example2() throws Exception{
throw new Exception("Please set the \"obj\"");
}
}
Điều đó thực sự phụ thuộc vào việc bạn có mong muốn tìm thấy đối tượng hay không. Nếu bạn theo trường phái suy nghĩ rằng các ngoại lệ nên được sử dụng để chỉ ra điều gì đó, tốt, lỗi, ngoại lệ đã xảy ra sau đó:
Nếu không, trả về null.