Khi nào sử dụng một khẳng định và khi nào sử dụng một ngoại lệ


121

Hầu hết thời gian tôi sẽ sử dụng một ngoại lệ để kiểm tra một điều kiện trong mã của mình, tôi tự hỏi khi nào là thời điểm thích hợp để sử dụng một xác nhận?

Ví dụ,

Group group=null;
try{
    group = service().getGroup("abc");
}catch(Exception e){
    //I dont log error because I know whenever error occur mean group not found
}

if(group !=null)
{
    //do something
}

Bạn có thể chỉ ra cách xác nhận phù hợp ở đây không? Tôi có nên sử dụng một khẳng định không?

Có vẻ như tôi không bao giờ sử dụng xác nhận trong mã sản xuất và chỉ thấy xác nhận trong các bài kiểm tra đơn vị. Tôi biết rằng trong hầu hết các trường hợp, tôi chỉ có thể sử dụng ngoại lệ để thực hiện việc kiểm tra như trên, nhưng tôi muốn biết cách phù hợp để thực hiện việc đó một cách "chuyên nghiệp".

Câu trả lời:


81

Các xác nhận nên được sử dụng để kiểm tra điều gì đó không bao giờ nên xảy ra, trong khi một ngoại lệ nên được sử dụng để kiểm tra điều gì đó có thể xảy ra.

Ví dụ: một hàm có thể chia cho 0, vì vậy nên sử dụng một ngoại lệ, nhưng một xác nhận có thể được sử dụng để kiểm tra xem ổ cứng có đột ngột biến mất hay không.

Một xác nhận sẽ dừng chương trình chạy, nhưng một ngoại lệ sẽ cho phép chương trình tiếp tục chạy.

Lưu ý rằng đó if(group != null)không phải là một khẳng định, đó chỉ là một điều kiện.


3
"một xác nhận có thể được sử dụng để kiểm tra xem ổ đĩa cứng có đột ngột tắt hay không" - Tôi muốn nói điều này là không chính xác: tại sao bạn muốn điều này được xử lý trong quá trình phát triển, mà không phải trong quá trình sản xuất (khi các xác nhận thường bị vô hiệu hóa)?
herman 14/09/13

71
Nhận xét về ổ cứng là sai. Các xác nhận là để kiểm tra lỗi trong logic mã của bạn. Đừng bao giờ sử dụng chúng để kiểm tra thứ gì đó mà bạn không kiểm soát được. Hãy nhớ rằng, nếu một xác nhận không thành công, điều đó có nghĩa là mã của bạn sai .
Ian Goldby 14/1113

1
@Marius có điều kiện của bạn có thể được thay thế bằng một khẳng định như thế này: nhóm assert = null
IgorGanapolsky

1
Bị phản đối vì ví dụ về ổ cứng mâu thuẫn với triết lý của riêng bạn. Ổ cứng "biến mất" (theo quan điểm của mã) thực sự có thể xảy ra trong thực tế - không có vấn đề gì xảy ra. Giống như @IanGoldby nói, các xác nhận nên hoàn toàn phụ thuộc vào những thứ do mã của bạn kiểm soát.
Vicky Chijwani

Một câu trả lời tốt hơn nhiều nếu được đăng bởi Gregory Pakosz, vui lòng đọc bài đăng đó.
ormurin

169

Ngoài suy nghĩ của tôi (danh sách có thể không đầy đủ và quá dài để phù hợp với một nhận xét), tôi sẽ nói:

  • sử dụng các ngoại lệ khi kiểm tra các tham số được truyền cho các phương thức và hàm tạo công khai hoặc được bảo vệ
  • sử dụng các ngoại lệ khi tương tác với người dùng hoặc khi bạn mong đợi mã khách hàng phục hồi sau một tình huống ngoại lệ
  • sử dụng các ngoại lệ để giải quyết các vấn đề có thể xảy ra
  • sử dụng xác nhận khi kiểm tra điều kiện trước, điều kiện sau và các bất biến của mã riêng / nội bộ
  • sử dụng xác nhận để cung cấp phản hồi cho chính bạn hoặc nhóm nhà phát triển của bạn
  • sử dụng các xác nhận khi kiểm tra những điều rất khó xảy ra nếu không, điều đó có nghĩa là có một vấn đề nghiêm trọng trong ứng dụng của bạn
  • sử dụng xác nhận để nêu những điều mà bạn (được cho là) ​​biết là đúng

Nói cách khác, các ngoại lệ giải quyết tính mạnh mẽ của ứng dụng của bạn trong khi các xác nhận giải quyết tính đúng đắn của nó.

Các xác nhận được thiết kế để dễ viết, bạn có thể sử dụng chúng ở hầu hết mọi nơi và tôi đang sử dụng quy tắc ngón tay cái này: một tuyên bố khẳng định càng trông ngu ngốc, thì nó càng có giá trị và càng được nhúng nhiều thông tin. Khi gỡ lỗi một chương trình hoạt động không đúng cách, bạn chắc chắn sẽ kiểm tra các khả năng thất bại rõ ràng hơn dựa trên kinh nghiệm của mình. Sau đó, bạn sẽ kiểm tra các vấn đề không thể xảy ra: đây chính xác là khi các xác nhận giúp ích rất nhiều và tiết kiệm thời gian.


53
Tôi thích cách bạn diễn đạt điều này: Các ngoại lệ giải quyết tính mạnh mẽ của ứng dụng của bạn trong khi các xác nhận giải quyết tính đúng đắn của nó .
M. Dudley

Tôi đã đăng cái này lên trang web của mình: pempek.net/articles/2013/11/16/assertions-or-exceptions
Gregory Pakosz 17/11/13

Và nếu bạn tình cờ tìm kiếm một thư viện C ++ tùy chỉnh khẳng định, tôi phát hành github.com/gpakosz/Assert
Gregory Pakosz

26

Hãy nhớ rằng các xác nhận có thể bị vô hiệu hóa trong thời gian chạy bằng cách sử dụng các tham số và bị vô hiệu hóa theo mặc định , vì vậy đừng tính đến chúng ngoại trừ mục đích gỡ lỗi.

Ngoài ra, bạn nên đọc bài viết về khẳng định của Oracle để xem thêm các trường hợp sử dụng - hoặc không sử dụng - khẳng định.


Huế hue hue Tôi đã tự hỏi tại sao mã của tôi bị lỗi trong Eclipse nhưng chạy tốt trên dòng lệnh.
Steve

15

Theo nguyên tắc chung:

  • Sử dụng các xác nhận để kiểm tra tính nhất quán nội bộ nếu ai đó tắt chúng đi. (Lưu ý rằngjava lệnh tắt tất cả các xác nhận theo mặc định.)
  • Sử dụng các bài kiểm tra thường xuyên cho bất kỳ loại kiểm tra nào không nên tắt. Điều này bao gồm các biện pháp kiểm tra phòng thủ nhằm chống lại thiệt hại tiềm ẩn do lỗi gây ra và mọi dữ liệu / yêu cầu xác thực / bất kỳ thứ gì được cung cấp bởi người dùng hoặc dịch vụ bên ngoài.

Đoạn mã sau từ câu hỏi của bạn không hợp lệ có thể có lỗi

try {
    group = service().getGroup("abc");
} catch (Exception e) {
    //i dont log error because i know whenever error occur mean group not found
}

Vấn đề là bạn KHÔNG biết rằng một ngoại lệ có nghĩa là nhóm không được tìm thấy. Cũng có thể service()cuộc gọi đã ném một ngoại lệ hoặc nó trả về nullsau đó gây ra a NullPointerException.

Khi bạn bắt một "dự kiến" ngoại lệ, bạn nên nắm bắt duy nhất ngoại lệ mà bạn đang mong đợi. Bằng cách bắt java.lang.Exception(và đặc biệt là không ghi lại nó), bạn đang làm cho việc chẩn đoán / gỡ lỗi sự cố trở nên khó khăn hơn và có khả năng cho phép ứng dụng gây ra nhiều thiệt hại hơn.


4

Chà, quay lại Microsoft, khuyến nghị là đưa ra các Ngoại lệ trong tất cả các API mà bạn cung cấp công khai và sử dụng Asserts trong tất cả các loại giả định bạn đưa ra về mã nội bộ. Đó là một định nghĩa hơi lỏng lẻo nhưng tôi đoán tùy thuộc vào mỗi nhà phát triển để vẽ đường.

Về việc sử dụng các Ngoại lệ, như tên đã nói, việc sử dụng chúng phải đặc biệt vì vậy đối với mã bạn trình bày ở trên, getGroupcuộc gọi sẽ trở lại nullnếu không có dịch vụ nào tồn tại. Ngoại lệ chỉ xảy ra nếu một liên kết mạng bị hỏng hoặc tương tự như vậy.

Tôi đoán kết luận là nhóm phát triển mỗi ứng dụng phải xác định ranh giới của khẳng định và ngoại lệ một chút.


IMHO, vấn đề với loại đề xuất đó là nó ổn miễn là ranh giới giữa các phần công khai và riêng tư của một API là khá cố định. Nếu bạn đang phát triển mã mới sau đó rằng ranh giới thường là khá chất lỏng ...
Len Holgate

Vâng bạn đã đúng. Đó là một hướng dẫn nhưng vào cuối ngày, nó lại nằm ngoài sự nhạy cảm của các lập trình viên. Tôi không nghĩ rằng có một dòng xác định cuối cùng cho những điều này, vì vậy tôi đoán bạn chỉ cần làm với những gì bạn nghĩ là đúng từ việc đọc vô số mã khác nhau.
rui

3

Theo tài liệu này http://docs.oracle.com/javase/6/docs/technotes/guides/language/assert.html#design-faq-general , "Câu lệnh khẳng định phù hợp với điều kiện tiên quyết không công khai, điều kiện sau và bất biến lớp Kiểm tra điều kiện công khai vẫn nên được thực hiện bằng cách kiểm tra bên trong các phương pháp dẫn đến các trường hợp ngoại lệ cụ thể được ghi lại, chẳng hạn như IllegalArgumentException và IllegalStateException. "

Nếu bạn muốn biết thêm về điều kiện tiên quyết, điều kiện hậu điều kiện và sự bất biến của lớp, hãy xem tài liệu này: http://docs.oracle.com/javase/6/docs/technotes/guides/language/assert.html#usage-conditions . Nó cũng chứa các ví dụ về cách sử dụng xác nhận.


1

Kiểm tra null sẽ chỉ bắt null gây ra sự cố, trong khi thử / bắt như bạn có, nó sẽ bắt được bất kỳ lỗi nào .

Nói chung, thử / bắt an toàn hơn, nhưng hơi chậm hơn, và bạn phải cẩn thận để nắm bắt tất cả các loại lỗi có thể xảy ra. Vì vậy, tôi sẽ nói rằng hãy sử dụng try / catch - một ngày nào đó mã getGroup có thể thay đổi và bạn có thể cần mạng lớn hơn đó.


1

Bạn có thể ghi nhớ sự khác biệt đơn giản này trong khi sử dụng chúng. Các ngoại lệ sẽ được sử dụng để kiểm tra các lỗi dự kiến ​​và không mong đợi được gọi là lỗi đã kiểm tra và chưa kiểm tra trong khi xác nhận được sử dụng chủ yếu cho mục đích gỡ lỗi tại thời điểm chạy để xem liệu các giả định có được xác thực hay không.


1

Thú thực là tôi hơi bối rối trước câu hỏi của bạn. Khi một điều kiện khẳng định không được đáp ứng, một ngoại lệ được ném ra. Thật khó hiểu, điều này được gọi là AssertionError . Lưu ý rằng nó không được chọn, chẳng hạn như (ví dụ) IllegalArgumentException được ném trong các trường hợp rất giống nhau.

Vì vậy, sử dụng các xác nhận trong Java

  1. là một phương tiện ngắn gọn hơn để viết điều kiện / khối ném
  2. cho phép bạn bật / tắt các kiểm tra này thông qua các tham số JVM. Thông thường, tôi sẽ luôn để các kiểm tra này, trừ khi chúng ảnh hưởng đến hiệu suất thời gian chạy hoặc có một hình phạt tương tự.

AssertionError là một lớp con của Lỗi không phải RuntimeException.
Stephen C

Ah. Tất nhiên. Tôi đã nghĩ đến được chọn / bỏ chọn. Hiện đã được sửa chữa
Brian Agnew

Nó giải thích các khẳng định là gì (theo quan điểm gây tranh cãi), nhưng không giải thích chính xác khi nào sử dụng chúng.
Karl Richter

1

Xem phần 6.1.2 (Xác định so với mã lỗi khác) trong tài liệu của Sun tại liên kết sau.

http://www.oracle.com/technetwork/articles/javase/javapch06.pdf

Tài liệu này đưa ra lời khuyên tốt nhất mà tôi đã thấy về thời điểm sử dụng các xác nhận. Trích dẫn từ tài liệu:

"Một nguyên tắc chung là bạn nên sử dụng một xác nhận cho những trường hợp ngoại lệ mà bạn muốn quên đi. Khẳng định là cách nhanh nhất để giải quyết và quên đi một điều kiện hoặc trạng thái mà bạn không mong đợi phải có đôi pho vơi."


0

Thật không may, xác nhận có thể bị vô hiệu hóa. Khi trong quá trình sản xuất, bạn cần tất cả sự trợ giúp có thể nhận được khi theo dõi điều gì đó không lường trước được, vì vậy khẳng định sẽ tự loại.

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.