Có phải là một thực hành tốt để bắt một ngoại lệ được kiểm tra và ném RuntimeException không?


70

Tôi đã đọc một số mã của một đồng nghiệp và thấy rằng anh ta thường nắm bắt các ngoại lệ khác nhau và sau đó luôn ném một 'RuntimeException' thay vào đó. Tôi luôn nghĩ rằng đây là thực hành rất xấu. Tôi có lầm không?


17
"Giá của các ngoại lệ được kiểm tra là vi phạm Nguyên tắc mở / Đóng. Nếu bạn ném ngoại lệ được kiểm tra từ một phương thức trong mã của bạn và mức bắt là ba cấp trên, bạn phải khai báo ngoại lệ đó trong chữ ký của từng phương thức giữa bạn và sản phẩm khai thác . Điều này có nghĩa là một sự thay đổi ở mức thấp của phần mềm có thể buộc thay đổi chữ ký ở nhiều cấp độ cao hơn. " TiếtRobert C. Martin, «Mã sạch», trang 107
Songo

6
Thật thú vị khi lưu ý rằng Jim Waldo chống lại các ngoại lệ không được kiểm soát trong "Java: Các bộ phận tốt" shop.oreilly.com/product/9780596803742.do nói rằng các lập trình viên trưởng thành chỉ nên ném các ngoại lệ được kiểm tra. Chúng tôi đã đọc nó trong JUG của chúng tôi chỉ 6 năm trước khi nó xuất hiện và có vẻ như đó là lời khuyên tốt! Bây giờ, với lập trình chức năng, các ngoại lệ được kiểm tra là hoàn toàn khó sử dụng. Các ngôn ngữ như Scala và Kotlin thậm chí không có chúng. Tôi cũng đã bắt đầu gói kiểm tra trong các trường hợp ngoại lệ không được kiểm tra.
GlenPeterson

@GlenPeterson bạn cũng có lời khuyên trong FP để tránh thực thi hoàn toàn và sử dụng các loại tổng thay thế
jk.

Ngoài ra còn có những trường hợp rõ ràng của giao diện chức năng: các giao diện chức năng dựng sẵn (ví dụ Function, Predicate, vv) không có parametrized ném mệnh đề. Điều này có nghĩa là bạn cần nắm bắt, bọc và lấy lại bất kỳ trường hợp ngoại lệ nào được kiểm tra trong vòng lặp bên trong của bất kỳ phương thức stream () nào. Điều đó tự nó đưa ra sự cân bằng cho tôi về việc liệu các ngoại lệ được kiểm tra có phải là một ý tưởng tồi hay không.
Joel Cornett

Không có gì sai khi tạo các lớp con tùy chỉnh của RuntimeException để truyền đạt ý nghĩa thông qua ngoại lệ của bạn.
Joel Cornett

Câu trả lời:


55

Tôi không biết đủ ngữ cảnh để biết liệu đồng nghiệp của bạn có làm điều gì đó không chính xác hay không, vì vậy tôi sẽ tranh luận về điều này theo nghĩa chung.

Tôi không nghĩ rằng luôn luôn là một thực tiễn không chính xác để biến các ngoại lệ được kiểm tra thành một số hương vị của ngoại lệ thời gian chạy . Các ngoại lệ được kiểm tra thường bị các nhà phát triển lạm dụnglạm dụng .

Rất dễ sử dụng các ngoại lệ được kiểm tra khi chúng không được sử dụng (điều kiện không thể phục hồi hoặc thậm chí kiểm soát luồng). Đặc biệt nếu một ngoại lệ được kiểm tra được sử dụng cho các điều kiện mà người gọi không thể phục hồi, tôi nghĩ rằng điều đó là hợp lý để biến ngoại lệ đó thành ngoại lệ thời gian chạy với một thông báo / trạng thái hữu ích. Thật không may trong nhiều trường hợp khi một người phải đối mặt với một điều kiện không thể phục hồi, họ có xu hướng có một khối bắt trống, đó là một trong những điều tồi tệ nhất bạn có thể làm. Gỡ lỗi một vấn đề như vậy là một trong những nỗi đau lớn nhất mà một nhà phát triển có thể gặp phải.

Vì vậy, nếu bạn nghĩ rằng bạn đang xử lý một điều kiện có thể phục hồi, thì nó nên được xử lý tương ứng và không nên biến ngoại lệ thành ngoại lệ thời gian chạy. Nếu một ngoại lệ được kiểm tra được sử dụng cho các điều kiện không thể phục hồi, biến nó thành một ngoại lệ thời gian chạy là hợp lý .


17
Trong hầu hết các ứng dụng thực tế, có rất ít điều kiện không thể phục hồi. Gần như có một số mức độ mà bạn có thể và nên nói "OK, hành động này không thành công, vì vậy chúng tôi hiển thị / ghi thông báo lỗi tốt và tiếp tục với / chờ đợi tiếp theo".
Michael Borgwardt

6
Đó là sự thật, @MichaelBorgwardt, nhưng nơi xử lý kiểu đó thường ở mức rất cao của ứng dụng, vì vậy, bất cứ khi nào tôi thấy các nhà phát triển "xử lý" ngoại lệ ở các cấp thấp hơn, thường sẽ dễ dàng loại bỏ xử lý của họ và chỉ xóa bỏ ngoại lệ trở lên Ví dụ, một khung web như JSF bắt các ngoại lệ ở mức cao nhất, in các thông điệp nhật ký và tiếp tục xử lý các yêu cầu khác (không nói xử lý mặc định là phù hợp, chỉ là một ví dụ).
DavidS

40

có thể là TỐT . Xin vui lòng đọc:

http://onjava.com/pub/a/onjava/2003/11/19/exceptions.html

Hầu hết thời gian, mã máy khách không thể làm bất cứ điều gì về SQLExceptions. Đừng ngần ngại chuyển đổi chúng thành các ngoại lệ không được kiểm soát. Hãy xem xét đoạn mã sau:

public void dataAccessCode(){
  try{
      ..some code that throws SQLException
  }catch(SQLException ex){
      ex.printStacktrace();
  }
} 

Khối bắt này chỉ ngăn chặn ngoại lệ và không làm gì cả. Lý do là không có gì khách hàng của tôi có thể làm về một nhận thức SQLEx. Làm thế nào về việc đối phó với nó theo cách sau?

public void dataAccessCode(){
   try{
       ..some code that throws SQLException
   }catch(SQLException ex){
       throw new RuntimeException(ex);
   }
} 

Điều này chuyển đổi SQLException thành RuntimeException. Nếu xảy ra lỗi SQLException, mệnh đề bắt sẽ ném RuntimeException mới. Các luồng thực hiện bị đình chỉ và ngoại lệ được báo cáo. Tuy nhiên, tôi không làm hỏng lớp đối tượng kinh doanh của mình bằng cách xử lý ngoại lệ không cần thiết, đặc biệt là vì nó không thể làm bất cứ điều gì về một nhận thức SQLEx. Nếu sản phẩm khai thác của tôi cần nguyên nhân ngoại lệ gốc, tôi có thể sử dụng phương thức getCause () có sẵn trong tất cả các lớp ngoại lệ kể từ JDK1.4.

Ném các ngoại lệ được kiểm tra và không thể phục hồi từ nó là không có ích.

Một số người thậm chí nghĩ rằng không nên sử dụng ngoại lệ được kiểm tra. Xem http://www.ibm.com/developerworks/java/l Library / j-jtp05254 / index.html

Gần đây, một số chuyên gia được đánh giá cao, bao gồm Bruce Eckel và Rod Johnson, đã công khai tuyên bố rằng mặc dù ban đầu họ hoàn toàn đồng ý với lập trường chính thống về các ngoại lệ được kiểm tra, họ đã kết luận rằng việc sử dụng ngoại lệ được kiểm tra độc quyền không phải là ý tưởng tốt như nó ban đầu xuất hiện và các ngoại lệ được kiểm tra đã trở thành một vấn đề đáng kể cho nhiều dự án lớn. Eckel có một cái nhìn cực đoan hơn, cho rằng không nên kiểm tra tất cả các ngoại lệ; Quan điểm của Johnson bảo thủ hơn, nhưng vẫn cho thấy rằng ưu tiên chính thống cho các ngoại lệ được kiểm tra là quá mức. (Điều đáng chú ý là các kiến ​​trúc sư của C #, người gần như chắc chắn có nhiều kinh nghiệm sử dụng công nghệ Java, đã chọn bỏ qua các ngoại lệ được kiểm tra từ thiết kế ngôn ngữ, làm cho tất cả các ngoại lệ không được kiểm tra ngoại lệ.

Cũng từ cùng một liên kết:

Quyết định sử dụng các ngoại lệ không được kiểm tra là một vấn đề phức tạp và rõ ràng là không có câu trả lời rõ ràng. Lời khuyên của Sun là không sử dụng chúng cho mục đích gì, phương pháp C # (mà Eckel và những người khác đồng ý) là sử dụng chúng cho mọi thứ. Những người khác nói, "có một nền tảng trung gian."


13

Không, bạn không sai. Thực hành của anh ấy là vô cùng sai lầm. Bạn nên ném một ngoại lệ nắm bắt vấn đề gây ra nó. RunTimeException là rộng và đạt. Nó phải là một NullPulumException, ArgumentException, v.v. Bất cứ điều gì mô tả chính xác những gì đã sai. Điều này cung cấp khả năng phân biệt các vấn đề mà bạn nên xử lý và để chương trình tồn tại so với các lỗi nên là kịch bản "Đừng vượt qua". Những gì anh ta đang làm chỉ tốt hơn một chút so với "On Error Resume Next" trừ khi có gì đó thiếu trong thông tin được cung cấp trong câu hỏi.


1
Cảm ơn đã gợi ý. Và điều gì sẽ xảy ra nếu anh ta ném một ngoại lệ tùy chỉnh mà anh ta đã thực hiện kế thừa trực tiếp từ RuntimeException?
RoflcoptrException 23/11/11

27
@Gary Buyn: nhiều người nghĩ rằng ngoại lệ được kiểm tra là một thử nghiệm thiết kế ngôn ngữ thất bại và chúng là những thứ nên được sử dụng một cách tiết kiệm, không phải là vấn đề của thói quen.
Michael Borgwardt

7
@Gary Buyn: Đây là một bài viết phác thảo khá tốt về cuộc tranh luận: ibm.com/developerworks/java/l Library / j-jtp05254 / index.html Cũng lưu ý rằng hơn 15 năm sau khi Java giới thiệu tính năng này, không có ngôn ngữ nào khác chấp nhận nó, và C ++ đã không dùng một tính năng tương tự.
Michael Borgwardt

7
@c_maker: Trên thực tế, Bloch chủ yếu thúc đẩy quan điểm chính thống, và nhận xét của bạn dường như chủ yếu là về việc sử dụng nhiều ngoại lệ, thời gian hơn. Quan điểm của tôi là lý do duy nhất hợp lệ để sử dụng một ngoại lệ được kiểm tra là vì một điều kiện mà bạn mong muốn tất cả người gọi sẽ xử lý ngay lập tức.
Michael Borgwardt

14
Không cần thiết ném ngoại lệ kiểm tra vi phạm đóng gói. Bạn sẽ làm gì, nếu một phương thức đơn giản như 'getAccounts ()' ném cho bạn một 'SQLException', 'NullPulumException' hoặc 'FileNotFoundException'? Bạn có thể xử lý nó? Bạn có thể chỉ cần 'bắt (Ngoại lệ e) {}' nó. Bên cạnh đó, những ngoại lệ - thực hiện cụ thể của nó! Nó không phải là một phần của hợp đồng! Tất cả bạn cần biết là đã có một lỗi . Và nếu thực hiện thay đổi thì sao? Đột nhiên, mọi thứ phải thay đổi, khiến phương thức không còn ném ra 'SQLException', mà thay vào đó là một 'ParseXMLException'!
KL

8

Nó phụ thuộc.

Thực hành này có thể thậm chí khôn ngoan . Có nhiều tình huống (ví dụ như trong phát triển web), trong đó nếu xảy ra trường hợp ngoại lệ, bạn không thể làm gì được (vì bạn không thể sửa chữa DB không nhất quán từ mã của mình :-), chỉ nhà phát triển mới có thể làm điều đó). Trong những tình huống này, sẽ là khôn ngoan khi bọc ngoại lệ bị ném vào một ngoại lệ thời gian chạy để lấy lại nó. Hơn bạn có thể bắt tất cả các ngoại lệ này trong một số lớp xử lý ngoại lệ, ghi nhật ký lỗi và hiển thị cho người dùng một số thông báo + mã lỗi cục bộ đẹp.

Mặt khác , nếu ngoại lệ không phải là thời gian chạy (được kiểm tra), nhà phát triển API cho biết, ngoại lệ này có thể giải quyết được và cần được sửa chữa. Nếu có thể, hơn bạn chắc chắn nên làm điều đó.

Giải pháp khác có thể là đưa lại ngoại lệ được kiểm tra này vào lớp gọi, nhưng nếu bạn không thể giải quyết nó, nơi xảy ra ngoại lệ, bạn cũng có thể không thể giải quyết nó ở đây ...


Bạn hy vọng rằng nhà phát triển API biết họ đang làm gì và sử dụng các ngoại lệ được kiểm tra tốt. Tôi bắt đầu thấy các API ủng hộ việc ném các ngoại lệ trong thời gian chạy đồng thời ghi lại nó để khách hàng có tùy chọn bắt nó nếu muốn.
c_maker

Một phương thức đưa ra một ngoại lệ thường không thể biết liệu người gọi có thể phục hồi từ nó hay không. Mặt khác, tôi sẽ đề xuất rằng một phương thức chỉ nên để một ngoại lệ được kiểm tra ném bởi một phương thức bên trong để thoát nếu biết tại sao phương thức bên trong lại ném ngoại lệ đó và lý do phù hợp với API của phương thức bên ngoài. Nếu một phương thức bên trong ném ra một ngoại lệ được kiểm tra một cách bất ngờ, hãy để nó nổi lên như một ngoại lệ được kiểm tra có thể khiến người gọi hiểu sai về những gì đã xảy ra.
supercat

2
Cảm ơn bạn đã đề cập đến exception handling layer- ví dụ: trong một ứng dụng web, bộ lọc.
Jake Toronto

5

Tôi muốn nhận được ý kiến ​​về điều này, nhưng tôi thấy có những lúc điều này không nhất thiết là thực hành xấu. (Hoặc cực kỳ tệ). Nhưng có lẽ tôi đã sai.

Thông thường, một API bạn đang sử dụng sẽ đưa ra một ngoại lệ mà bạn không thể tưởng tượng được thực sự bị ném vào usecase cụ thể của mình. Trong trường hợp này, có vẻ hoàn toàn ổn khi ném RuntimeException với ngoại lệ bị bắt là nguyên nhân. Nếu ngoại lệ này được đưa ra, nó có thể sẽ là nguyên nhân gây ra lỗi lập trình và không nằm trong giới hạn cho thông số kỹ thuật chính xác.

Giả sử RuntimeException sau đó không bị bắt và bị bỏ qua, nó không ở đâu gần OnErrorResumeNext.

OnErrorResumeNext sẽ xảy ra khi ai đó bắt gặp một ngoại lệ và chỉ cần bỏ qua nó hoặc chỉ in nó ra. Đây là thực tế tồi tệ trong hầu hết các trường hợp.


Đây có thể là trường hợp gần đầu cây gọi, điều duy nhất bạn có thể làm là cố gắng phục hồi một cách duyên dáng và biết lỗi cụ thể sẽ không thực sự có ích. Trong trường hợp đó, bạn có thể phải ghi lại lỗi và tiếp tục (xử lý bản ghi tiếp theo, thông báo cho người dùng rằng đã xảy ra lỗi, v.v.). Nếu không, không. Bạn nên luôn luôn xử lý các trường hợp ngoại lệ gần với lỗi như thực tế, không bao bọc chúng như một con voi trắng cho người xử lý tiếp theo.
Michael K

@MichaelK Vấn đề là "gần với lỗi như thực tế" trong thực tế thường có nghĩa là "thông qua một số lớp can thiệp nằm ngoài tầm kiểm soát trực tiếp của bạn". Ví dụ, nếu lớp của tôi phải thực hiện một giao diện nhất định, tay tôi bị trói. Điều đó có thể xảy ra tùy ý sâu trong cây gọi. Ngay cả khi các giao diện nằm dưới sự kiểm soát của tôi, việc thêm các khai báo ném có thể làm cho sự trừu tượng bị rò rỉ nếu chỉ có một bộ triển khai cụ thể có thể có thể hình dung được. Làm cho mọi khách hàng phải trả chi phí cho các chi tiết triển khai của một số ít không phải là một IMO đánh đổi thiết kế tuyệt vời.
Tim Seguine

4

TL; DR

Tiền đề

  • Các ngoại lệ thời gian chạy nên được ném khi lỗi không thể phục hồi: khi lỗi nằm trong mã và không phụ thuộc vào trạng thái bên ngoài (do đó, việc khôi phục sẽ sửa mã).
  • Các ngoại lệ được kiểm tra nên được ném khi mã chính xác, nhưng trạng thái bên ngoài không như mong đợi: không tìm thấy kết nối mạng, không tìm thấy tệp hoặc bị hỏng, v.v.

Phần kết luận

Chúng tôi có thể lấy lại một ngoại lệ được kiểm tra dưới dạng ngoại lệ thời gian chạy nếu mã lan truyền hoặc mã giao diện giả định rằng việc triển khai cơ bản phụ thuộc vào trạng thái bên ngoài, khi nó rõ ràng là không.


Phần này thảo luận về chủ đề khi một trong hai trường hợp ngoại lệ nên được ném. Bạn có thể bỏ qua thanh ngang tiếp theo nếu bạn chỉ muốn đọc một lời giải thích chi tiết hơn cho kết luận.

Khi nào thì thích hợp để ném ngoại lệ thời gian chạy? Bạn ném ngoại lệ thời gian chạy khi rõ ràng mã không chính xác và việc khôi phục đó là phù hợp bằng cách sửa đổi mã.

Ví dụ, thích hợp để ném ngoại lệ thời gian chạy cho các mục sau:

float nan = 1/0;

Điều này sẽ ném một phân chia bởi ngoại lệ thời gian chạy bằng không. Điều này là thích hợp vì bị lỗi.

Hoặc, ví dụ, đây là một phần của hàm HashMaptạo:

public HashMap(int initialCapacity, float loadFactor) {
    if (initialCapacity < 0)
        throw new IllegalArgumentException("Illegal initial capacity: " + initialCapacity);
    if (initialCapacity > MAXIMUM_CAPACITY)
        initialCapacity = MAXIMUM_CAPACITY;
    if (loadFactor <= 0 || Float.isNaN(loadFactor))
        throw new IllegalArgumentException("Illegal load factor: " +
                loadFactor);
    // more irrelevant code...
}

Để khắc phục dung lượng hoặc hệ số tải ban đầu, bạn nên chỉnh sửa mã để đảm bảo rằng các giá trị chính xác đang được truyền vào. Nó không phụ thuộc vào một số máy chủ ở xa, trên trạng thái hiện tại của đĩa, một tập tin, hoặc một chương trình khác Hàm tạo đó được gọi với các đối số không hợp lệ phụ thuộc vào tính chính xác của mã gọi, có thể là một phép tính sai dẫn đến các tham số không hợp lệ hoặc luồng không phù hợp bị lỗi.

Khi nào thì thích hợp để ném một ngoại lệ được kiểm tra? Bạn ném một ngoại lệ được kiểm tra khi sự cố thể phục hồi mà không thay đổi mã. Hoặc để đặt nó theo các thuật ngữ khác nhau, bạn ném một ngoại lệ được kiểm tra khi lỗi liên quan đến trạng thái trong khi mã là chính xác.

Bây giờ từ "phục hồi" có thể là khó khăn ở đây. Điều đó có thể có nghĩa là bạn tìm một cách khác để đạt được mục tiêu: Chẳng hạn, nếu máy chủ không phản hồi thì bạn nên thử máy chủ tiếp theo. Nếu loại phục hồi đó có thể phù hợp với trường hợp của bạn thì đó là điều tuyệt vời, nhưng đó không phải là điều duy nhất có nghĩa là phục hồi - phục hồi có thể chỉ đơn giản là hiển thị hộp thoại báo lỗi cho người dùng giải thích điều gì đã xảy ra hoặc nếu đó là ứng dụng máy chủ thì đó có thể là gửi email đến quản trị viên, hoặc thậm chí chỉ đơn thuần là ghi lại lỗi một cách thích hợp và chính xác.

Hãy lấy ví dụ đã được đề cập trong câu trả lời của mrmuggles:

public void dataAccessCode(){
   try{
       ..some code that throws SQLException
   }catch(SQLException ex){
       throw new RuntimeException(ex);
   }
}

Đây không phải là cách chính xác để xử lý ngoại lệ được kiểm tra. Các đơn thuần không đủ năng lực để xử lý các ngoại lệ trong này phạm vi của phương pháp không có nghĩa là các ứng dụng nên bị rơi. Thay vào đó, nó là thích hợp để tuyên truyền nó đến một phạm vi cao hơn như vậy:

public Data dataAccessCode() throws SQLException {
    // some code that communicates with the database
}

Cho phép khả năng phục hồi của người gọi:

public void loadDataAndShowUi() {
    try {
        Data data = dataAccessCode();
        showUiForData(data);
    } catch(SQLException e) {
        // Recover by showing an error alert dialog
        showCantLoadDataErrorDialog();
    }
}

Các ngoại lệ được kiểm tra là một công cụ phân tích tĩnh, chúng làm rõ cho lập trình viên những gì có thể sai trong một cuộc gọi nhất định mà không yêu cầu họ tìm hiểu việc thực hiện hoặc trải qua quá trình thử và sai. Điều này giúp dễ dàng đảm bảo rằng không có phần nào của luồng lỗi sẽ bị bỏ qua. Lấy lại một ngoại lệ được kiểm tra như một ngoại lệ thời gian chạy đang hoạt động chống lại tính năng phân tích tĩnh tiết kiệm lao động này.

Một điều đáng nói nữa là lớp gọi có bối cảnh tốt hơn về sơ đồ lớn hơn của những thứ như đã được chứng minh ở trên. Có thể có nhiều nguyên nhân cho điều đó dataAccessCodesẽ được gọi, lý do cụ thể cho cuộc gọi chỉ hiển thị đối với người gọi - do đó có thể đưa ra quyết định tốt hơn khi khôi phục chính xác khi thất bại.

Bây giờ chúng ta đã có sự khác biệt rõ ràng này, chúng ta có thể tiến hành suy luận khi chấp nhận lại một ngoại lệ được kiểm tra như một ngoại lệ thời gian chạy.


Đưa ra ở trên, khi nào thì thích hợp để lấy lại một ngoại lệ được kiểm tra là RuntimeException? Khi mã bạn đang sử dụng giả định sự phụ thuộc vào trạng thái bên ngoài, nhưng bạn có thể khẳng định rõ ràng rằng nó không phụ thuộc vào trạng thái bên ngoài.

Hãy xem xét những điều sau đây:

StringReader sr = new StringReader("{\"test\":\"test\"}");
try {
    doesSomethingWithReader(sr); // calls #read, so propagates IOException
} catch (IOException e) {
    throw new IllegalStateException(e);
}

Trong ví dụ này, mã đang lan truyền IOExceptionvì API của Readerđược thiết kế để truy cập trạng thái bên ngoài, tuy nhiên chúng tôi biết rằng StringReaderviệc triển khai không truy cập trạng thái bên ngoài. Ở phạm vi này, nơi chúng tôi chắc chắn có thể khẳng định rằng các phần liên quan đến cuộc gọi không truy cập IO hoặc bất kỳ trạng thái bên ngoài nào khác, chúng tôi có thể lấy lại ngoại lệ một cách an toàn như một ngoại lệ trong thời gian chạy mà không gây ngạc nhiên cho các đồng nghiệp không biết về việc triển khai của chúng tôi (và có thể giả sử rằng mã truy cập IO sẽ ném một IOException).


Lý do để kiểm tra nghiêm ngặt các ngoại lệ phụ thuộc trạng thái bên ngoài là chúng không có tính xác định (không giống như các ngoại lệ phụ thuộc logic, có thể dự đoán sẽ được sao chép mỗi lần cho một phiên bản của mã). Ví dụ: nếu bạn cố chia cho 0, bạn sẽ luôn tạo ra một ngoại lệ. Nếu bạn không chia cho 0, bạn sẽ không bao giờ tạo ra ngoại lệ và bạn không phải xử lý trường hợp ngoại lệ đó, vì điều đó sẽ không bao giờ xảy ra. Tuy nhiên, trong trường hợp truy cập một tệp, thành công một lần không có nghĩa là bạn sẽ thành công vào lần tới - người dùng có thể đã thay đổi quyền, một quy trình khác có thể đã xóa hoặc sửa đổi nó. Vì vậy, bạn luôn phải xử lý trường hợp đặc biệt đó, hoặc bạn có thể có một lỗi.


2

Đối với các ứng dụng độc lập. Khi bạn biết ứng dụng của mình không thể xử lý ngoại lệ bạn có thể, thay vì ném RuntimeException đã kiểm tra, ném Lỗi, hãy để ứng dụng gặp sự cố, hy vọng báo cáo lỗi và sửa ứng dụng của bạn. (Xem câu trả lời của mrmuggles để biết thêm thảo luận chuyên sâu về pro và con của kiểm tra so với không được kiểm tra.)


2

Đây là thực tế phổ biến trong nhiều khuôn khổ. Ví dụ, Hibernatelàm chính xác điều này. Ý tưởng là các API không nên xâm phạm vào phía máy khách và Exceptionbị xâm nhập vì bạn phải viết mã rõ ràng để xử lý chúng tại nơi bạn gọi api. Nhưng nơi đó có thể không phải là nơi thích hợp để xử lý ngoại lệ ở nơi đầu tiên.
Thực ra, thành thật mà nói đây là một chủ đề "nóng" và nhiều tranh chấp vì vậy tôi sẽ không đứng về phía nào nhưng tôi sẽ nói rằng những gì bạn của bạn làm / đề xuất không phải là không bình thường hoặc không phổ biến.


1

Toàn bộ điều "ngoại lệ được kiểm tra" là một ý tưởng tồi.

Lập trình có cấu trúc chỉ cho phép thông tin được truyền qua giữa các hàm (hoặc, theo cách nói của Java, các phương thức) khi chúng ở "gần". Chính xác hơn, thông tin chỉ có thể di chuyển qua các chức năng theo hai cách:

  1. Từ một người gọi đến một callee, thông qua đối số đi qua.

  2. Từ một callee đến người gọi của nó, như các giá trị trả về.

Đây là một điều cơ bản tốt. Đây là những gì cho phép bạn suy luận về mã của mình cục bộ: nếu bạn cần hiểu hoặc sửa đổi một phần chương trình của mình, bạn chỉ cần xem phần đó và các phần "lân cận" khác.

Tuy nhiên, trong một số trường hợp, cần gửi thông tin đến chức năng "ở xa" mà không có ai ở giữa "biết". Điều này là chính xác khi các ngoại lệ phải được sử dụng. Một ngoại lệ là một thông điệp bí mật được gửi từ một trình tăng cường (bất kỳ phần nào trong mã của bạn có thể chứa một throwcâu lệnh) đến một trình xử lý (bất kỳ phần nào trong mã của bạn có thể chứa một catchkhối tương thích với ngoại lệ đó là thrown).

Các trường hợp ngoại lệ được kiểm tra phá hủy sự bí mật của cơ chế, và, với nó, chính lý do cho sự tồn tại của nó. Nếu một hàm có thể đủ khả năng để cho người gọi của nó "biết" một phần thông tin, chỉ cần gửi trực tiếp phần thông tin đó như một phần của giá trị trả về.


Có thể là tốt để đề cập rằng loại vấn đề này thực sự có thể tàn phá trong trường hợp một phương thức chạy một chức năng được cung cấp bởi người gọi của nó. Tác giả của phương thức nhận hàm trong nhiều trường hợp sẽ không có lý do để biết cũng như không quan tâm người gọi đang mong đợi nó làm gì, cũng không có ngoại lệ nào mà người gọi có thể mong đợi. Nếu mã nhận được phương thức không mong muốn nó đưa ra một ngoại lệ được kiểm tra, thì phương thức được cung cấp có thể phải bọc bất kỳ ngoại lệ được kiểm tra nào, nó sẽ đưa ra các ngoại lệ không được kiểm tra mà nhà cung cấp của nó có thể nắm bắt được.
supercat

0

Điều này có thể phụ thuộc vào từng trường hợp. Trong một số trường hợp nhất định, bạn nên làm những gì bạn của bạn đang làm, ví dụ như khi bạn đưa ra một api cho một số khách hàng và bạn muốn khách hàng ít biết về các chi tiết triển khai, nơi bạn biết rằng các ngoại lệ thực hiện nhất định có thể cụ thể đối với chi tiết thực hiện và không thể tiếp xúc với khách hàng.

Bằng cách tránh các trường hợp ngoại lệ được kiểm tra, bạn có thể tiết lộ các ứng dụng cho phép khách hàng viết mã sạch hơn vì chính khách hàng có thể xác nhận trước các điều kiện ngoại lệ.

Ví dụ Integer.parseInt (String) lấy một chuỗi và trả về số nguyên tương đương với nó và ném NumberFormatException trong trường hợp chuỗi không phải là số. Bây giờ hãy tưởng tượng việc gửi biểu mẫu với một trường ageđược chuyển đổi thông qua phương thức này nhưng khách hàng đã đảm bảo xác thực về phần của nó, vì vậy không có điểm nào buộc phải kiểm tra ngoại lệ.


0

Thực sự có một vài câu hỏi ở đây

  1. Bạn có nên chuyển đổi các ngoại lệ được kiểm tra thành những trường hợp không được kiểm tra?

Nguyên tắc chung là các trường hợp ngoại lệ mà người gọi dự kiến ​​sẽ bắt và khôi phục từ cần được kiểm tra. Các trường hợp ngoại lệ khác (những trường hợp mà kết quả hợp lý duy nhất là hủy bỏ toàn bộ hoạt động hoặc nơi bạn cho rằng chúng không đủ khả năng khiến việc lo lắng về việc xử lý chúng cụ thể là không đáng) nên được bỏ qua.

Đôi khi phán đoán của bạn về việc một ngoại lệ có đáng bị bắt và phục hồi khác với API mà bạn đang làm việc không. Đôi khi các vấn đề bối cảnh, một ngoại lệ đáng để xử lý trong một tình huống có thể không đáng để xử lý trong một tình huống khác. Đôi khi bàn tay của bạn bị ép buộc bởi các giao diện hiện có. Vì vậy, có những lý do chính đáng để biến một ngoại lệ được kiểm tra thành một ngoại lệ không được kiểm tra (hoặc thành một loại ngoại lệ được kiểm tra khác)

  1. Nếu bạn định biến một ngoại lệ không được kiểm tra thành một ngoại lệ được kiểm tra thì bạn nên làm thế nào.

Đầu tiên và quan trọng nhất là đảm bảo bạn sử dụng thiết bị xích ngoại lệ. Bằng cách đó, thông tin từ ngoại lệ ban đầu không bị mất và có thể được sử dụng để gỡ lỗi.

Thứ hai, bạn phải quyết định loại ngoại lệ để sử dụng. Sử dụng một runtimeexception đơn giản làm cho người gọi khó xác định điều gì sai nhưng nếu người gọi đang cố xác định điều gì sai có thể là một dấu hiệu cho thấy bạn không nên thay đổi ngoại lệ để không kiểm tra.


0

Trong một câu hỏi boolean, thật khó để trả lời khác nhau sau hai câu trả lời gây tranh cãi, nhưng tôi muốn đưa ra một viễn cảnh thậm chí được đề cập ở một vài nơi, nó không đủ nhấn mạnh về tầm quan trọng của nó.

Với nhiều năm tôi thấy rằng luôn có ai đó bối rối về một vấn đề tầm thường, họ đang thiếu hiểu biết về một số nguyên tắc cơ bản.

Phân lớp. Một ứng dụng phần mềm (ít nhất là được cho là) ​​một đống các lớp chồng lên nhau. Một kỳ vọng quan trọng để phân lớp tốt là các lớp thấp hơn cung cấp chức năng cho nhiều thành phần có khả năng từ lớp trên.

Giả sử ứng dụng của bạn có các lớp sau từ dưới lên NET, TCP, HTTP, REST, MÔ HÌNH DỮ LIỆU, KINH DOANH.

Nếu lớp doanh nghiệp của bạn muốn thực hiện cuộc gọi nghỉ ngơi ... hãy đợi trong giây lát. Tại sao tôi lại nói vậy? Tại sao tôi không nói yêu cầu HTTP hoặc giao dịch TCP hoặc gửi các gói mạng? Bởi vì những thứ đó không liên quan đến tầng lớp doanh nghiệp của tôi. Tôi sẽ không xử lý chúng, tôi sẽ không xem xét chi tiết về chúng. Tôi hoàn toàn ổn nếu họ ở sâu trong các trường hợp ngoại lệ mà tôi nhận được là nguyên nhân và tôi không muốn biết rằng chúng thậm chí còn tồn tại.

Hơn nữa, thật tệ nếu tôi biết chi tiết, bởi vì nếu ngày mai tôi muốn thay đổi các giao thức vận chuyển gạch chân xử lý các chi tiết dành riêng cho giao thức TCP có nghĩa là việc trừu tượng REST của tôi không làm tốt việc trừu tượng hóa từ đó việc thực hiện cụ thể.

Khi chuyển một ngoại lệ từ lớp này sang lớp khác, điều quan trọng là phải xem lại mọi khía cạnh của nó và ý nghĩa của nó đối với sự trừu tượng mà lớp hiện tại cung cấp. Nó có thể là để thay thế ngoại lệ bằng khác, nó có thể kết hợp một số ngoại lệ. Nó cũng có thể chuyển đổi chúng từ kiểm tra sang không được kiểm tra hoặc ngược lại.

Tất nhiên, là những nơi thực tế mà bạn đề cập có ý nghĩa là một câu chuyện khác nhau, nhưng nói chung - vâng, nó có thể là một điều tốt để làm.


-2

Theo ý kiến ​​của tôi,

Ở cấp độ khung, chúng ta nên bắt ngoại lệ thời gian chạy để giảm thêm khối lượng thử bắt cho kẻ xâm lược ở cùng một nơi.

Ở cấp độ ứng dụng, chúng tôi hiếm khi nắm bắt các ngoại lệ thời gian chạy và tôi nghĩ rằng thực tế này là xấu.


1
Và bạn sẽ làm gì với những ngoại lệ đó ở cấp độ khung?
Matthieu

Nếu có một lớp UI trong khung có thể xử lý các trường hợp ngoại lệ, thì UI sẽ đưa ra một thông báo lỗi thuộc loại nào đó có lỗi. Trong trường hợp ứng dụng javascript một trang, ứng dụng có thể hiển thị thông báo lỗi. Cấp, lớp UI chỉ xử lý lỗi nếu lớp sâu hơn thực sự không thể phục hồi từ lỗi.
Jake Toronto
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.