2017-11-16 27 views
5

Xuất thân từ một thế giới PHP, nơi chỉ có một cách để viết ngoại lệ xử lý .. Tôi tìm thấy gói ngoại lệ trong Java một chút "xấu xí":Gói ngoại lệ Java: thực hành không tốt?

public void exampleOneException(String input) throws MyBusinessException { 
    try { 
     // do something 
    } catch (NumberFormatException e) { 
     throw new MyBusinessException("Error...", e); 
    } 
} 

Tôi thích sử dụng phong cách này thay vì:

public void exampleTwoException() { 
    try { 
     // do something 
    } catch (MyBusinessException e) { 
     log.error("Error...: " + e); 
    } catch (NumberFormatException e) { 
     log.error("Error...: " + e); 
    } 
} 

Có bất kỳ sự khác biệt hoặc thực tiễn tốt nhất nào về các cách tiếp cận đó trong việc xử lý ngoại lệ không?

+0

Phương thức gọi là 'exampleOneException' có thể bắt' MyBusinessException' và xử lý theo yêu cầu riêng của chúng. Các phương thức gọi 'exampleTwoException' không có cách nào để biết rằng mọi thứ đã sai. – khelwood

+2

Đây là những điều cơ bản khác nhau. Cách tiếp cận bạn nên sử dụng tùy thuộc vào cách bạn muốn phản hồi các lỗi - ví dụ: chỉ cần đăng nhập chúng cục bộ, chuyển tiếp chúng hoặc logic kinh doanh chuyên dụng thay đổi hành vi khi xảy ra lỗi. –

+1

Hai đoạn mã đó không hoạt động giống nhau. Trong một trong những bạn đang rethrowing ngoại lệ trong khác bạn chỉ cần đăng nhập nó. –

Trả lời

5

Đây là cả hai phương pháp hợp lệ cho hai trường hợp khác nhau.

Trong trường hợp đầu tiên, phương pháp không thể làm bất cứ điều gì thông minh về ngoại lệ, nhưng nó phải "báo cáo" nó lên trên. Bằng cách đó, người gọi có thể bắt ngoại lệ và quyết định phải làm gì với nó (ví dụ như hủy luồng, bật thông báo cho người dùng, đăng nhập, v.v.)

Trong trường hợp thứ hai, bạn bắt ngoại lệ và đăng nhập nó, có hiệu quả che giấu sự thất bại từ người gọi. Kiểu xử lý này có thể hữu ích nếu người gọi không thực sự quan tâm nếu hoạt động thành công hay không.

+1

Bạn cũng có thể viết không có gì vào để bắt khoản nếu bạn chắc chắn rằng các hoạt động sẽ thành công.Nó được coi là chương trình xấu nhưng vẫn được sử dụng rộng rãi đơn giản bởi vì mọi người lười biếng ^^ –

1

Tôi có thể nói cả hai số NumberFormatExceptionMyBusinessException đều hữu ích nhưng trong các trường hợp khác nhau.

Chúng thường xuất hiện ở các cấp phân cấp lớp khác nhau: ví dụ: NumberFormatException là ngoại lệ cấp thấp hơn và bạn có thể không muốn hiển thị nó ở cấp cao hơn (ví dụ: giao diện người dùng) nếu người dùng không có khả năng khôi phục từ nó. Trong trường hợp này, bạn chỉ cần ném MyBusinessException và hiển thị thông báo cung cấp thông tin, ví dụ như một bước nào đó trong bước trước bị cung cấp sai hoặc xảy ra lỗi xử lý nội bộ và cần khởi động lại quy trình. Mặt khác, nếu chức năng của bạn được sử dụng ở mức trung cấp (ví dụ: API) và nhà phát triển có phương tiện khôi phục từ hành vi ngoại lệ, NumberFormatException hữu ích hơn, vì nó có thể được xử lý theo chương trình và dòng chảy của ứng dụng có thể tiếp tục với gián đoạn tối thiểu (ví dụ như cung cấp một số hợp lệ mặc định). Ngoài ra, điều này có thể cho biết một lỗ hổng/lỗi trong mã nên được sửa.

Để biết chi tiết về cách thực hiện theo phương pháp hay nhất trong việc sử dụng ngoại lệ, hãy đọc Item 61 - Throw exceptions appropriate to the abstraction từ Effective Java by Joshua Bloch.

3

Ví dụ đầu tiên thường được xem là cách tiếp cận tốt hơn.

Bạn cũng nên không xem xét các MyBusinessExceptiongói các NumberFormatException, mà là NumberFormatExceptionnguyên nhân của MyBusinessException.

Ngoại lệ phải phù hợp với giao diện đang được hiển thị. Người gọi giao diện của bạn không cần phải biết hoặc xử lý các chi tiết triển khai. Trừ khi NumberFormatException thực sự có ý nghĩa như một loại lỗi khi gọi số exampleOneException, nó phải được dịch sang một ngoại lệ phù hợp hơn.

Ví dụ cụ thể hơn thường bao gồm các triển khai khác nhau, nơi người dùng không cần giao diện để xử lý các chi tiết cụ thể (có thể không được biết lúc biên dịch).

interface MyRepository { 
    Object read(int id) throws ObjectNotFoundException; 
} 

// a sql backed repository 
class JdbcRepository implements MyRepository { 
    public Object read(int id) throws ObjectNotFoundException { 
     try { ... } 
     catch (SQLException ex) { 
      throw new ObjectNotFoundException(ex); 
     } 
    } 
} 

// a file backed repository 
class FileRepository implements MyRepository { 
    public Object read(int id) throws ObjectNotFoundException { 
     try { ... } 
     catch (FileNotFoundException ex) { 
      throw new ObjectNotFoundException(ex) 
     } 
    } 
} 

Vì giao diện khai báo các loại lỗi có thể trả về, khách hàng của giao diện đó có thể nhất quán và hợp lý. Thêm mã để xử lý FileNotFoundExceptionSQLException thì việc triển khai thực tế có thể là hoặc không, không phải là tuyệt vời.

Cân nhắc xem có nhiều địa điểm trong việc triển khai FileRepository có thể ném FileNotFoundException hay không. Điều đó có ngụ ý mỗi và mọi người trong số họ ngụ ý đối tượng không được tìm thấy?

Khi xem xét tùy chọn hai, exampleTwoException, điều quan trọng là nhận ra rằng khối catch của bạn cho biết hiệu quả của bất kỳ lỗi nào xảy ra đã được giảm nhẹ. Có trường hợp ngoại lệ kiểm tra sẽ được bỏ qua một cách hợp lý, nhưng nó là nhiều khả năng rằng một đơn giản

try { ... } 
catch (SomeException ex) { 
    log.error("caught some exception", ex); 
} 

thực sự là kết quả của một nhà phát triển không cân nhắc những hậu quả của ngoại lệ, hoặc mã nên tôi đã bao gồm một FIXME.

Điều này thậm chí còn đúng hơn khi bạn thấy catch (Exception ex) hoặc catch (Throwable ex) không thể chấp nhận được. Và cuối cùng, bạn có muốn trở thành người đào sâu thông qua một ứng dụng tìm kiếm tất cả các địa điểm mà bạn cần thêm (chưa) một khối đánh bắt khác để xử lý triển khai mới không? Không. Có lẽ đó là một nguyên nhân của catch (Exception ex) ...

Luôn ngoại lệ ném phù hợp với trừu tượng

0

Từ góc nhìn của mã sạch và vững chắc bạn không lái xe đến đúng hướng.
Trước hết, bạn không cần sử dụng try/catch cho số Exception đã được tiêu đề của phương thức này ném. Vì vậy, trong trường hợp đầu tiên của bạn mã phải như sau:

public void exampleOneException(String input) throws MyBusinessException { 
    // do something 
} 

Đây là đẹp hơn phải không? Trong trường hợp trên, nếu MyBusinessException xảy ra thì ngoại lệ sẽ bị nuốt để người dùng sẽ không bao giờ thấy điều gì đã xảy ra. Nếu bạn muốn điều này xảy ra thì đây là thực hành tốt nhất của bạn.

Trường hợp thứ hai là rõ ràng. Bạn nắm bắt hai ngoại lệ khác nhau và bạn xử lý chúng (bằng cách đăng nhập), đây là cách thực hành tốt hơn nói chung.

Vì vậy, cách thực hành tốt nhất trong hai trường hợp khác nhau của bạn phụ thuộc vào những gì bạn muốn đạt được. Về cái đẹp, điều này hoàn toàn chủ quan.

+0

try/catch là cần thiết khi tôi cần sử dụng cuối cùng: như thể tôi muốn đóng kết nối DB thay vì dựa vào bộ thu gom rác Java. – numediaweb

+0

Đơn giản chỉ cần giải thích: try/catch là cần thiết trong trường hợp bạn muốn xử lý một ngoại lệ. Nếu bạn không quan tâm đến việc truyền hoặc xử lý thì bạn có thể sử dụng 'throws' trên tiêu đề của phương thức. Sử dụng 'cuối cùng' giả định một ý định xử lý. –

+0

tốt nếu chúng ta quan tâm đến chất lượng mã và do đó sử dụng một thư viện linting (sonarlint) sau đó sử dụng cuối cùng là một "thực hành tốt" hoặc chúng tôi sẽ nhận được hiển thị một lỗi: "Tài nguyên nên được đóng cửa" .. nhưng đó là một chủ đề khác .. – numediaweb

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