2010-02-11 35 views
17

Có tốt hơn để trả lại giá trị null hoặc ném ngoại lệ từ phương thức API không?thiết kế java api - NULL hoặc Exception

Trả về giá trị rỗng yêu cầu kiểm tra rỗng không hợp lệ trên toàn bộ và gây ra vấn đề nghiêm trọng về chất lượng nếu trả lại không được chọn. Việc ném một ngoại lệ buộc người dùng phải mã hóa điều kiện bị lỗi, nhưng vì các ngoại lệ Java phát sinh và buộc mã người gọi xử lý chúng, nói chung, sử dụng ngoại lệ tùy chỉnh có thể là một ý tưởng tồi (đặc biệt trong java).

Bất kỳ lời khuyên thiết thực và âm thanh nào?

+0

Bạn có biết về mẫu đối tượng Null không? http://en.wikipedia.org/wiki/Null_Object_pattern –

Trả lời

5

Tôi nghĩ câu trả lời hoàn toàn phụ thuộc vào ngữ cảnh của đơn đăng ký của bạn và điều bạn coi là "trường hợp ngoại lệ" so với lỗi lập trình viên.

Ví dụ, nếu viết một phân tích cú pháp XML, bạn có thể chọn để thực hiện các phương pháp như:

/** 
* Returns first child element with matching element name or else 
* throws an exception. Will never return null. 
*/ 
Element getMandatoryChildElement(Element parent, String elementName) 
throws MissingElementException; 

... như cách tiếp cận này cho phép bạn thực hiện thông điệp của bạn phân tích mã trong một khối duy nhất của logic, mà không cần phải để kiểm tra xem thông báo có được tạo đúng hay không sau khi truy xuất từng phần tử hoặc thuộc tính.

+0

@Adamski - Tôi thích điều này. Nhưng nếu người gọi đã ném ngoại lệ, ngoại lệ này sẽ bị tắt tiếng đúng không? Ví dụ: Tôi đã thấy NumberFormatException không được xử lý bởi vì người gọi đã ném ngoại lệ. Vì vậy, có một quy tắc hệ quả - một phương pháp không bao giờ nên ném "Ngoại lệ"? –

+0

Tôi nghĩ rằng nếu không tìm thấy phần tử, người gọi không nên gọi phương thức ngay từ đầu. Bạn có thể có một phương pháp kiểm tra, vì vậy bạn có thể viết nếu (hasElement ('test')) getMandatoryChildElement ('test') ;. Tốt hơn nhiều so với phương pháp tiếp cận thử-catch-try tiếp theo của mã. Làm thế nào bạn sẽ viết một phương pháp 'tải tập tin'? –

+0

@disown: sự cố đang đưa ra một phương thức trong API. Bạn không biết người sử dụng, vì vậy bạn phải chắc chắn rằng anh ta sẽ không làm những điều xấu, và nói với anh ấy khi anh ta làm sai. @ srini.venigalla: nếu người gọi đã ném Ngoại lệ, anh ấy sẽ sớm phát hiện ra nó ở đâu đó. –

1

Nó thực sự phụ thuộc vào cách bạn muốn tình huống đó được xử lý. Nếu nó thực sự là một điều kiện lỗi thì hãy ném một ngoại lệ. Nếu null là một đầu ra chấp nhận được thì chỉ đơn thuần là viết tài liệu và cho phép người dùng xử lý nó.

6

Nếu null là giá trị trả lại có thể chấp nhận được (nghĩa là, nó có thể là kết quả của các đầu vào hợp lệ thay vì trường hợp lỗi), sau đó trả lại null; nếu không thì hãy ném một ngoại lệ.

Tôi hoàn toàn không hiểu tuyên bố của bạn rằng ngoại lệ được kiểm tra có thể là một ý tưởng tồi bởi vì người gọi buộc phải xử lý chúng - đây là toàn bộ ngoại lệ được kiểm tra. Nó bảo vệ chống lại các điều kiện lỗi lan truyền vào mã mà không được xử lý đúng cách.

+0

@danben - Tôi thực sự không thích một API mà ném nửa tá ngoại lệ tùy chỉnh và tôi phải thêm một chồng các trình xử lý ngoại lệ không có liên quan. Bản thân điều đó không phải là vấn đề, nhưng nếu API thêm một ngoại lệ mới, tất cả chuỗi thượng lưu cần được cập nhật. Tôi có thể hiểu tất cả những gì cần thiết hoặc thậm chí là một điều tốt, nhưng tôi đang tìm kiếm các quy ước rõ ràng để tuân theo. –

+1

@ srini.venigalla - một nửa tá thường là quá nhiều. Hãy nhớ rằng không có lý do để nhất thiết phải có bất kỳ ngoại lệ ** tùy chỉnh ** nào - bạn có thể ném 'IllegalArgumentException' hoặc một cái gì đó khác phù hợp với nhu cầu của bạn. – danben

+0

Lợi ích của việc có ngoại lệ được kiểm tra tùy chỉnh như AxisException hoặc SoapException hoặc WhateverException là gì? Tất cả những gì bạn có thể suy ra từ điều này là ngoại lệ đến từ lib, mà bạn vẫn biết từ stacktrace. Nếu bạn muốn thực hiện xử lý lỗi tất cả, nó sẽ dễ dàng được thêm vào trong máy khách. –

1

Nếu bạn có lỗi thư viện nội bộ (không mong muốn), hãy để các ngoại lệ thời gian chạy lan truyền. Nếu có lỗi trong mã thư viện, máy khách sẽ bị lỗi. Sửa lỗi trong thư viện. Đừng bắt tất cả và trả về một ngoại lệ cụ thể cho thư viện, điều này sẽ không làm được gì tốt.

Nếu bạn mong đợi mọi thứ (đôi khi) xảy ra sai, hãy tạo điều này vào API. Điều này dựa trên nguyên tắc mà bạn không nên sử dụng ngoại lệ cho luồng chương trình thông thường.

Ví dụ:

ProcessResult performLibraryTask(TaskSpecification ts) 

Bằng cách này bạn có thể có ProcessResult biết tình trạng lỗi:

ProcessResult result = performLibraryTask(new FindSmurfsTaskSpecification(SmurfColor.BLUE)); 
if (result.failed()) { 
    throw new RuntimeException(result.error()); 
} 

Cách tiếp cận này cũng tương tự như cách tiếp cận trở lại null, nhưng bạn có thể gửi thêm thông tin trở lại khách hàng.

EDIT:

Đối với tương tác mà không phù hợp với các giao thức đồng ý, bạn có thể ném lỗi thời gian chạy, mà bạn có thể tài liệu. Ví dụ:

if (currentPeriod().equals(SmurfColor.BLUE) && SmurfColor.GREEN.equals(taskSpecification.getSmurfColor()) { 
    throw new IllegalStateException("Cannot search for green smurfs during blue period, invalid request"); 
} 

Xin lưu ý rằng điều này là do tương tác vi phạm hợp đồng và không được dự kiến ​​xảy ra.

+0

Tôi thích nó, nhưng cách tiếp cận này giống như sự trở lại null, cầu xin cho lòng thương xót của người gọi. –

+0

Đồng ý. Bạn sẽ phải đặt giới hạn cho các lỗi mà bạn cho là có khả năng xảy ra trong quá trình sử dụng bình thường và mô hình các lỗi này là không ngoại lệ. Lỗi mã hóa (vi phạm hợp đồng) hoặc những thứ bạn không thấy xảy ra trong quá trình sử dụng bình thường (như hết bộ nhớ, dereferencing con trỏ null), bạn nên sử dụng ngoại lệ cho. –

2

Tôi có xu hướng trở về null để "getXXX" phương pháp, ví dụ, lấy một cái gì đó từ ví dụ cơ sở dữ liệu : Người dùng getUserByName (String name) là OK cho tôi để trở về null

Nhưng, nếu bạn cần những gì bạn đang tìm nạp bằng cách gọi phương thức, hãy ném một ngoại lệ: ví dụ: getDatabaseConnection() phải luôn trả về kết nối và không bao giờ trả lại null

Và, đối với các phương thức trả về bộ sưu tập hoặc mảng, KHÔNG BAO GIỜ trả về null. trả lại bộ sưu tập/mảng trống thay vì

+0

Tôi thường sử dụng findXXX cho các phương thức có thể trả về giá trị rỗng và getXXX cho những người không sử dụng. –

+0

@disown - điều đó sẽ không chuẩn và không cho phép sử dụng thư viện và mã java bean. – Robin

+0

@Robin: Nếu bạn đang viết một API cơ sở dữ liệu? Javabeans dành cho các lớp loại DTO đơn giản. Bạn không nghiêm túc đặt tên cho tất cả các phương pháp của bạn theo đặc tả đậu java? –

1

Đồng ý với những người khác, nếu null là giá trị trả lại chấp nhận được, trả lại giá trị đó, nhưng ngược lại thì không.

Điều tôi muốn thêm, là bạn cũng có thể phân biệt giữa các ngoại lệ đã chọn và không được chọn. Trong một số trường hợp, bạn có thể nên sử dụng ngoại lệ không được kiểm soát, ví dụ: người dùng API của bạn có thể không ở trong tình huống để xử lý ngoại lệ đó. Spring Framework thường sử dụng các ngoại lệ không được kiểm tra, điều này làm cho mã thường dễ đọc hơn, nhưng đôi khi khó theo dõi lỗi hơn.

6

Josh Bloch đề xuất trả lại đối tượng trống hoặc đối tượng "đơn vị" thay cho giá trị null. Hãy xem Effective Java để biết hàng tấn mẩu tin lưu niệm Java hữu ích.

3

Trong Practial API Design, tác giả khuyên không trả lại null giá trị (see p.24).

Điểm chính của ông là nó dẫn đến sự gia tăng kiểm tra đối với null giá trị và nhiều tài liệu hơn.

Tôi cũng khuyên bạn nên đọc "Null References: The Billion Dollar Mistake" của cơ quan có thẩm quyền trong lĩnh vực này (T. Hoare).

+0

+1 cho bạn - Nếu chỉ có ai đó có thể đặt một giá trị khoảng thời gian để sửa chữa các vấn đề về trống và các vấn đề về memcpy .. –

+0

* tỷ đô la: P – Jeriko

+0

@ Jeriko bạn nói đúng. đã sửa. – ewernli

4

Quan điểm của tôi là

Đừng bao giờ quay trở lại NULL khi trở lại loại là bộ sưu tập hoặc một mảng. Nếu không có giá trị trả lại, sau đó trả về một tập hợp trống hoặc một mảng trống.

Điều này giúp loại bỏ sự cần thiết phải kiểm tra trống.

Thói quen tốt:

public List<String> getStudentList() 
{ 
    int count = getStudetCount(); 
    List<String> studentList = new ArrayList<String>(count); 
    //Populate studentList logic 
    return studentList; 
} 

Bad Thực hành:. Không bao giờ viết mã như dưới đây.

public List<String> getStudentList() 
{ 
    int count = getStudetCount(); 
    if(count == 0) 
    { 
     return null; 
    } 
    //Populate studentList logic 
    return studentList; 
} 
+1

Nếu 'studentList' được cho là không thay đổi: bạn nên kiểm tra' count' được trả về bởi 'getStudentCount()' và sau đó sử dụng 'Collections.emptyList()' nếu nó là 0. Điều này cho phép tối ưu hóa tiềm năng (tức là có một danh sách trống được lưu trong bộ nhớ cache được trả về mỗi lần). Nếu 'count' không phải là 0, bạn vẫn nên bọc danh sách trả về trong một' Collections.unmodifiableList (studentList) 'wrapper để đảm bảo nó không bị vô tình sửa đổi. – cdmckay

0

Tôi thường xem xét ví dụ tương tự trong JDK.

Trong trường hợp có người truy cập, bạn nên sử dụng Null.

Null nên được sử dụng để biểu thị dữ liệu bị thiếu hoặc không xác định.

Đối với mọi thứ khác, nó thường đại diện cho lỗi trong quá trình xử lý.

Hiện tại, chúng tôi có 2 tùy chọn, thời gian chạy so với thời gian biên dịch. Dưới đây là những ví dụ về cách tôi tìm kiếm các chất tương tự.

Cho phép lấy ví dụ từ ArrayIndexOutOfBoundException. Việc truy cập hoặc biến đổi một phần tử là một hoạt động rất phổ biến.

nếu được kiểm tra, khi đó mã tiêu thụ sẽ rất lộn xộn. Ngoài ra, trong các nhà phát triển nói chung có xu hướng nhận thức được tình hình có thể dẫn đến ArrayIndexOutOfBounException và nó có thể tránh được bằng cách viết mã tốt.

Trên flipside, nếu chúng tôi có một đoạn mã như mở tệp, có thể do phụ thuộc bên ngoài (thiếu tệp hoặc không có đủ đặc quyền) và không bắt nguồn từ lỗi trong chương trình, đó là ý tưởng hay để có ngoại lệ được kiểm tra.

Các vấn đề liên quan