Cách hiện đại để thực hiện xử lý lỗi


118

Tôi đã suy nghĩ vấn đề này một thời gian và thấy mình liên tục tìm thấy sự cảnh báo và mâu thuẫn, vì vậy tôi hy vọng ai đó có thể đưa ra kết luận cho những điều sau:

Ngoại lệ ủng hộ mã lỗi

Theo như tôi biết, từ bốn năm làm việc trong ngành, đọc sách và blog, v.v ... cách tốt nhất hiện nay để xử lý lỗi là ném ngoại lệ, thay vì trả lại mã lỗi (không nhất thiết phải là mã lỗi, nhưng là loại đại diện cho một lỗi).

Nhưng - với tôi điều này dường như mâu thuẫn ...

Mã hóa giao diện, không triển khai

Chúng tôi mã hóa các giao diện hoặc trừu tượng để giảm khớp nối. Chúng tôi không biết hoặc muốn biết loại cụ thể và cách triển khai giao diện. Vậy làm thế nào chúng ta có thể biết những ngoại lệ nào chúng ta nên tìm cách nắm bắt? Việc thực hiện có thể ném ra 10 ngoại lệ khác nhau hoặc không thể ném ra. Khi chúng tôi bắt gặp một ngoại lệ chắc chắn chúng tôi sẽ đưa ra các giả định về việc thực hiện?

Trừ khi - giao diện có ...

Thông số kỹ thuật ngoại lệ

Một số ngôn ngữ cho phép các nhà phát triển tuyên bố rằng một số phương thức nhất định đưa ra một số ngoại lệ nhất định (ví dụ Java, sử dụng throwstừ khóa.) Từ quan điểm của mã gọi, điều này có vẻ ổn - chúng tôi biết rõ những ngoại lệ nào chúng tôi có thể cần nắm bắt.

Nhưng - điều này dường như gợi ý một ...

Trừu tượng rò rỉ

Tại sao một giao diện nên chỉ định ngoại lệ nào có thể được ném? Điều gì xảy ra nếu việc triển khai không cần ném ngoại lệ hoặc cần ném ngoại lệ khác? Ở cấp độ giao diện, không có cách nào để biết ngoại lệ nào mà việc triển khai có thể muốn ném.

Vì thế...

Để kết luận

Tại sao các ngoại lệ được ưa thích khi chúng dường như (trong mắt tôi) mâu thuẫn với các thực tiễn tốt nhất của phần mềm? Và, nếu mã lỗi quá tệ (và tôi không cần phải bán trên các tật xấu của mã lỗi), liệu có cách nào khác không? Trạng thái hiện tại (hoặc sắp có) của xử lý lỗi đáp ứng các yêu cầu của thực tiễn tốt nhất như đã nêu ở trên, nhưng không dựa vào mã gọi kiểm tra giá trị trả về của mã lỗi?


12
Tôi không nhận được tranh luận của bạn về trừu tượng rò rỉ. Chỉ định ngoại lệ nào mà một phương thức cụ thể có thể ném là một phần của đặc tả giao diện . Giống như loại giá trị trả về là một phần của đặc tả giao diện. Các ngoại lệ chỉ không "đi ra từ phía bên trái" của chức năng, nhưng chúng vẫn là một phần của giao diện.
lừa dối

@deceze Làm thế nào một giao diện có thể nói lên những gì một triển khai có thể ném? Nó có thể ném tất cả các ngoại lệ trong hệ thống loại! Và nhiều ngôn ngữ không hỗ trợ thông số kỹ thuật ngoại lệ cho thấy chúng khá tinh ranh
RichK

1
Tôi đồng ý rằng thật khó để quản lý tất cả các ngoại lệ khác nhau mà một phương thức có thể đưa ra, nếu chính nó sử dụng các phương thức khác và không nắm bắt được ngoại lệ của chúng trong nội bộ. Phải nói rằng, đó không phải là một mâu thuẫn.
lừa dối

1
Giả định rò rỉ ở đây là mã có thể xử lý một ngoại lệ khi thoát khỏi ranh giới giao diện. Điều đó rất khó xảy ra, vì nó không biết gần như đủ về những gì thực sự đã sai. Tất cả những gì nó biết là đã xảy ra sự cố và việc triển khai giao diện không biết cách xử lý. Tỷ lệ cược rằng nó có thể làm một công việc tốt hơn là kém, ngoài việc báo cáo nó và chấm dứt chương trình. Hoặc bỏ qua nó nếu giao diện không quan trọng đối với hoạt động chương trình thích hợp. Một chi tiết triển khai mà bạn không thể và không nên mã hóa trong hợp đồng giao diện.
Hans Passant

Câu trả lời:


31

Trước hết, tôi sẽ không đồng ý với tuyên bố này:

Ngoại lệ ủng hộ mã lỗi

Điều này không phải lúc nào cũng đúng: ví dụ, hãy xem Objective-C (với khung Foundation). Ở đó NSError là cách ưa thích để xử lý lỗi, bất chấp sự tồn tại của thứ mà nhà phát triển Java gọi là ngoại lệ thực sự: @try, @catch, @throw, lớp NSException, v.v.

Tuy nhiên, sự thật là nhiều giao diện rò rỉ sự trừu tượng của chúng với các ngoại lệ được đưa ra. Tôi tin rằng đây không phải là lỗi của kiểu "ngoại lệ" trong việc truyền / xử lý lỗi. Nói chung, tôi tin rằng lời khuyên tốt nhất về xử lý lỗi là:

Xử lý lỗi / ngoại lệ ở mức thấp nhất có thể, thời gian

Tôi nghĩ rằng nếu một người tuân theo quy tắc đó, thì lượng "rò rỉ" từ trừu tượng có thể rất hạn chế và bị hạn chế.

Về việc liệu các ngoại lệ được ném bởi một phương thức có phải là một phần của khai báo hay không, tôi tin rằng chúng nên: chúng là một phần của hợp đồng được xác định bởi giao diện này: Phương thức này thực hiện A hoặc thất bại với B hoặc C.

Ví dụ, nếu một lớp là Trình phân tích cú pháp XML, một phần trong thiết kế của nó sẽ chỉ ra rằng tệp XML được cung cấp hoàn toàn sai. Trong Java, bạn thường làm như vậy bằng cách khai báo các ngoại lệ mà bạn muốn gặp và thêm chúng vào throwsphần khai báo của phương thức. Mặt khác, nếu một trong các thuật toán phân tích thất bại, không có lý do gì để vượt qua ngoại lệ đó ở trên.

Tất cả tập trung vào một điều: Thiết kế giao diện tốt. Nếu bạn thiết kế giao diện của mình đủ tốt, không có ngoại lệ nào sẽ ám ảnh bạn. Mặt khác, nó không chỉ là ngoại lệ sẽ làm phiền bạn.

Ngoài ra, tôi nghĩ rằng những người tạo ra Java có lý do bảo mật rất mạnh để bao gồm các ngoại lệ cho một khai báo / định nghĩa phương thức.

Một điều cuối cùng: Một số ngôn ngữ, ví dụ Eiffel, có các cơ chế khác để xử lý lỗi và đơn giản là không bao gồm khả năng ném. Ở đó, một 'ngoại lệ' của loại được tự động đưa ra khi hậu điều kiện cho một thói quen không được thỏa mãn.


12
+1 để phủ nhận "Ngoại lệ ưu tiên đối với mã lỗi." Tôi đã được dạy rằng các ngoại lệ là tốt và tốt, miễn là trên thực tế, đó là các ngoại lệ và không phải là quy tắc. Gọi một phương thức đưa ra một ngoại lệ để xác định xem một điều kiện có đúng hay không là một thực tế cực kỳ tồi tệ.
Neil

4
@JoshuaDrake: Anh ấy chắc chắn sai. Ngoại lệ và gotorất khác nhau. Ví dụ: các ngoại lệ luôn đi theo cùng một hướng - xuống ngăn xếp cuộc gọi. Và thứ hai, bảo vệ bản thân khỏi các trường hợp ngoại lệ bất ngờ hoàn toàn giống với thực tế như DRY - ví dụ, trong C ++, nếu bạn sử dụng RAII để đảm bảo dọn dẹp, thì nó đảm bảo dọn dẹp trong mọi trường hợp, không chỉ ngoại lệ mà còn tất cả các luồng điều khiển thông thường. Điều này là vô cùng đáng tin cậy hơn. try/finallyhoàn thành một cái gì đó tương tự Khi bạn đảm bảo dọn dẹp đúng cách, bạn không cần phải coi ngoại lệ là trường hợp đặc biệt.
DeadMG

4
@JoshuaDrake Xin lỗi, nhưng Joel đang ở đó. Các ngoại lệ không giống như goto, bạn luôn tăng ít nhất một cấp lên ngăn xếp cuộc gọi. Và bạn sẽ làm gì nếu mức độ trên không thể xử lý ngay lập tức với mã lỗi? Trả lại mã lỗi khác? Một phần của vấn đề với lỗi là chúng CÓ THỂ bị bỏ qua, dẫn đến các vấn đề tồi tệ hơn là ném ngoại lệ.
Andy

25
-1 vì tôi hoàn toàn không đồng ý với "Xử lý lỗi / ngoại lệ ở mức thấp nhất có thể" - đó là sai, khoảng thời gian. Xử lý các lỗi / ngoại lệ ở mức độ thích hợp . Thông thường, đó là một mức độ cao hơn nhiều, và trong trường hợp đó, mã lỗi là một nỗi đau rất lớn. Lý do để ưu tiên các ngoại lệ đối với mã lỗi là vì chúng cho phép bạn tự do lựa chọn ở cấp độ nào để xử lý chúng mà không ảnh hưởng đến các mức ở giữa.
Michael Borgwardt

2
@Giorgio: xem artima.com/intv/handcuffs.html - đặc biệt là trang 2 và 3.
Michael Borgwardt

27

Tôi chỉ muốn lưu ý rằng các ngoại lệ và mã lỗi không phải là cách duy nhất để xử lý lỗi và các đường dẫn mã thay thế.

Ngoài ý muốn, bạn có thể có một cách tiếp cận giống như phương pháp của Haskell, trong đó các lỗi có thể được báo hiệu thông qua các kiểu dữ liệu trừu tượng với nhiều hàm tạo (nghĩ enum phân biệt hoặc con trỏ null, nhưng an toàn và có khả năng thêm cú pháp chức năng đường hoặc trợ giúp để làm cho dòng mã nhìn tốt).

func x = do
    a <- operationThatMightFail 10
    b <- operationThatMightFail 20
    c <- operationThatMightFail 30
    return (a + b + c)

operThatMightfail là một hàm trả về giá trị được bọc trong Có thể. Nó hoạt động giống như một con trỏ rỗng, nhưng ký hiệu đảm bảo rằng toàn bộ điều ước tính thành null nếu bất kỳ a, b hoặc c nào thất bại. (và trình biên dịch bảo vệ bạn khỏi tạo một NullPulumException tình cờ)

Một khả năng khác là truyền đối tượng xử lý lỗi dưới dạng đối số phụ cho mọi hàm bạn gọi. Trình xử lý lỗi này có một phương thức cho mỗi "ngoại lệ" có thể được báo hiệu bởi hàm bạn truyền cho nó và có thể được sử dụng bởi hàm đó để xử lý các ngoại lệ khi chúng xảy ra, mà không nhất thiết phải tua lại ngăn xếp thông qua các ngoại lệ.

LISP thông thường thực hiện điều này và làm cho nó khả thi bằng cách có hỗ trợ tổng hợp (đối số ngầm) và có các hàm tích hợp theo giao thức này.


1
Điều đó thực sự gọn gàng. Cảm ơn bạn đã trả lời phần về các lựa chọn thay thế :)
RichK

1
BTW, ngoại lệ là một trong những phần chức năng nhất của ngôn ngữ mệnh lệnh hiện đại. Ngoại lệ tạo thành một đơn nguyên. Ngoại lệ sử dụng kết hợp patter. Ngoại lệ giúp bắt chước phong cách ứng dụng mà không thực sự học những gì Maybe.
9000

Tôi đã đọc một cuốn sách về SML gần đây. Nó đã đề cập đến các loại tùy chọn, ngoại lệ (và tiếp tục). Lời khuyên là sử dụng các loại tùy chọn khi trường hợp không xác định được dự kiến ​​sẽ xảy ra khá thường xuyên và sử dụng ngoại lệ khi trường hợp không xác định xảy ra rất hiếm khi xảy ra.
Giorgio

8

Có, ngoại lệ có thể gây ra trừu tượng rò rỉ. Nhưng các mã lỗi thậm chí không tồi tệ hơn về vấn đề này?

Một cách để giải quyết vấn đề này là để giao diện xác định chính xác trường hợp ngoại lệ nào có thể được ném trong trường hợp nào và tuyên bố rằng việc triển khai phải ánh xạ mô hình ngoại lệ bên trong của chúng sang đặc tả này, bằng cách bắt, chuyển đổi và ném lại ngoại lệ nếu cần. Nếu bạn muốn có một giao diện "hoàn hảo", đó là cách nên làm.

Trong thực tế, thường chỉ đủ để xác định các ngoại lệ là một phần logic của giao diện và khách hàng có thể muốn nắm bắt và làm gì đó. Nói chung, mọi người đều hiểu rằng có thể có các ngoại lệ khác khi xảy ra lỗi ở mức độ thấp hoặc có biểu hiện lỗi và khách hàng chỉ có thể xử lý chung bằng cách hiển thị thông báo lỗi và / hoặc tắt ứng dụng. Ít nhất ngoại lệ vẫn có thể chứa thông tin giúp chẩn đoán vấn đề.

Trong thực tế, với các mã lỗi, khá nhiều điều tương tự đã xảy ra, chỉ là theo kiểu ngầm định hơn, và nhiều khả năng thông tin sẽ bị mất và ứng dụng kết thúc ở trạng thái không nhất quán.


1
Tôi không hiểu tại sao mã lỗi có thể gây ra sự trừu tượng bị rò rỉ. Nếu mã lỗi được trả về thay vì giá trị trả về thông thường và hành vi này được mô tả trong đặc tả của hàm / phương thức, thì IMO không có rò rỉ. Hay tôi đang nhìn một cái gì đó?
Giorgio

Nếu mã lỗi dành riêng cho việc triển khai trong khi API được coi là không thể thực hiện, thì việc chỉ định và trả lại mã lỗi sẽ rò rỉ các chi tiết triển khai không mong muốn. Ví dụ điển hình: API ghi nhật ký với triển khai dựa trên tệp và dựa trên DB, trong đó trước đây có thể có lỗi "đầy đĩa" và lỗi "kết nối DB bị từ chối bởi máy chủ".
Michael Borgwardt

Tôi hiểu ý của bạn Nếu bạn muốn báo cáo các lỗi cụ thể về triển khai thì API không thể là bất khả tri khi thực hiện (không có mã lỗi cũng như ngoại lệ). Tôi đoán giải pháp duy nhất sẽ là xác định mã lỗi không xác định triển khai như "tài nguyên không khả dụng" hoặc quyết định rằng API không phải là bất khả tri.
Giorgio

1
@Giorgio: Có, báo cáo các lỗi cụ thể về triển khai là khó khăn với API không thể thực hiện. Tuy nhiên, bạn có thể làm điều đó với các ngoại lệ, bởi vì (không giống như mã lỗi) chúng có thể có nhiều trường. Vì vậy, bạn có thể sử dụng loại ngoại lệ để cung cấp thông tin lỗi chung (ResourceMissingException) và bao gồm mã / thông báo lỗi cụ thể theo triển khai như một trường. Tốt nhất của cả hai thế giới :-).
sleske

Và BTW, đó chỉ là những gì java.lang.SQLException làm. Nó có getSQLState(chung) và getErrorCode(nhà cung cấp cụ thể). Bây giờ nếu chỉ có nó có các lớp con thích hợp ...
sleske

5

Có rất nhiều thứ hay ho ở đây, tôi chỉ muốn nói thêm rằng tất cả chúng ta nên cảnh giác với mã sử dụng các ngoại lệ như một phần của luồng điều khiển thông thường. Đôi khi mọi người rơi vào cái bẫy mà mọi thứ không phải là trường hợp thông thường đều trở thành ngoại lệ. Tôi thậm chí đã thấy một ngoại lệ được sử dụng như một điều kiện chấm dứt vòng lặp.

Trường hợp ngoại lệ có nghĩa là "một cái gì đó tôi không thể xử lý ở đây đã xảy ra, cần phải đi ra ngoài để người khác tìm ra phải làm gì." Người dùng nhập dữ liệu không hợp lệ không phải là ngoại lệ (cần được xử lý cục bộ bởi đầu vào bằng cách hỏi lại, v.v.).

Một trường hợp thoái hóa khác của việc sử dụng ngoại lệ mà tôi đã thấy là những người có phản ứng đầu tiên là "ném ngoại lệ". Điều này hầu như luôn luôn được thực hiện mà không cần viết bắt (quy tắc ngón tay cái: viết bắt trước, sau đó là câu ném). Trong các ứng dụng lớn, điều này trở nên có vấn đề khi một ngoại lệ chưa được phát hiện xuất hiện từ các vùng liên kết và làm nổ tung chương trình.

Tôi không chống ngoại lệ, nhưng chúng có vẻ như là những người độc thân của một vài năm trước: được sử dụng quá thường xuyên và không phù hợp. Chúng hoàn hảo cho mục đích sử dụng, nhưng trường hợp đó không rộng như một số người nghĩ.


4

Trừu tượng rò rỉ

Tại sao một giao diện nên chỉ định ngoại lệ nào có thể được ném? Điều gì xảy ra nếu việc triển khai không cần ném ngoại lệ hoặc cần ném ngoại lệ khác? Ở cấp độ giao diện, không có cách nào để biết ngoại lệ nào mà việc triển khai có thể muốn ném.

Không. Các đặc tả ngoại lệ nằm trong cùng một nhóm với các kiểu trả về và đối số - chúng là một phần của giao diện. Nếu bạn không thể tuân thủ đặc điểm kỹ thuật đó, thì đừng thực hiện giao diện. Nếu bạn không bao giờ ném, thì tốt thôi. Không có gì rò rỉ về việc chỉ định các ngoại lệ trong một giao diện.

Mã lỗi vượt quá xấu. Họ thật kinh khủng. Bạn phải nhớ thủ công để kiểm tra và tuyên truyền chúng, mọi lúc, cho mỗi cuộc gọi. Điều này vi phạm DRY, để bắt đầu và ồ ạt thổi mã xử lý lỗi của bạn. Sự lặp lại này là một vấn đề lớn hơn nhiều so với bất kỳ trường hợp ngoại lệ nào. Bạn không bao giờ có thể âm thầm bỏ qua một ngoại lệ, nhưng mọi người có thể và âm thầm bỏ qua các mã trả lại - chắc chắn là một điều xấu.


Mã lỗi có thể dễ sử dụng hơn nếu bạn có các hình thức phương pháp đường trợ giúp hoặc cú pháp tốt, và trong một số ngôn ngữ, bạn có thể đảm bảo trình biên dịch và hệ thống loại đảm bảo rằng bạn sẽ không bao giờ quên xử lý mã lỗi. Về phần giao diện ngoại lệ, tôi nghĩ rằng anh ta đã suy nghĩ về sự lộn xộn khét tiếng của các ngoại lệ được kiểm tra của Java. Mặc dù chúng có vẻ như là một ý tưởng hoàn toàn hợp lý thoạt nhìn, chúng gây ra nhiều vấn đề nhỏ trong thực tế.
hugomg

@missingno: Đó là bởi vì, như thường lệ, Java có sự triển khai khủng khiếp của chúng, không phải vì các ngoại lệ được kiểm tra vốn đã xấu.
DeadMG

1
@missingno: Những loại vấn đề có thể được gây ra bởi các ngoại lệ được kiểm tra?
Giorgio

@Giorgio: Rất khó để thấy trước mọi loại ngoại lệ mà một phương thức có thể đưa ra, vì điều này cần phải tính đến các lớp con và mã khác chưa được viết. Trong thực tế, mọi người kết thúc với các cách giải quyết xấu xí, vứt bỏ thông tin, như sử dụng lại một lớp ngoại lệ hệ thống cho mọi thứ hoặc phải thường xuyên bắt và ném lại các ngoại lệ bên trong. Tôi cũng đã nghe nói rằng các ngoại lệ được kiểm tra là một trở ngại lớn khi họ đang cố gắng thêm các chức năng ẩn danh vào ngôn ngữ.
hugomg

@missingno: Các hàm ẩn danh của AFAIK trong Java chỉ là đường cú pháp cho các lớp bên trong ẩn danh với chính xác một phương thức, vì vậy tôi không chắc tại sao tôi hiểu tại sao các ngoại lệ được kiểm tra sẽ là một vấn đề (nhưng tôi thừa nhận tôi không biết nhiều về chủ đề này). Vâng, thật khó để thấy trước những ngoại lệ mà một phương thức sẽ đưa ra, đó là lý do tại sao IMO có thể hữu ích khi kiểm tra các ngoại lệ, để bạn không phải đoán. Tất nhiên bạn có thể viết mã kém để xử lý chúng, nhưng điều này bạn cũng có thể làm với các ngoại lệ không được kiểm soát. Tuy nhiên, tôi biết rằng cuộc tranh luận khá phức tạp và thành thật mà nói tôi thấy những ưu và nhược điểm ở cả hai phía.
Giorgio

2

Xử lý ngoại lệ tốt có thể có thực hiện giao diện riêng của họ. Tùy thuộc vào loại ngoại lệ được ném, thực hiện các bước mong muốn.

Giải pháp cho vấn đề thiết kế của bạn là có hai triển khai giao diện / trừu tượng hóa. Một cho các chức năng và một cho xử lý ngoại lệ. Và tùy thuộc vào loại Ngoại lệ bị bắt, hãy gọi lớp loại ngoại lệ phù hợp.

Việc thực thi mã Lỗi là một cách xử lý ngoại lệ chính thống. Nó giống như việc sử dụng chuỗi so với trình tạo chuỗi.


4
nói cách khác: việc triển khai ném các lớp con của các ngoại lệ được xác định trong api.
rút cooke

2

Các trường hợp ngoại lệ IM-rất-HO phải được đánh giá theo từng trường hợp, bởi vì bằng cách phá vỡ dòng kiểm soát, chúng sẽ làm tăng độ phức tạp thực tế và nhận thức của mã của bạn, trong nhiều trường hợp không cần thiết như vậy. Tạm gác lại cuộc thảo luận liên quan đến việc ném ngoại lệ vào bên trong các chức năng của bạn - điều thực sự có thể cải thiện luồng kiểm soát của bạn, nếu người ta xem xét việc ném ngoại lệ qua ranh giới cuộc gọi, hãy xem xét các điều sau:

Cho phép một callee phá vỡ dòng kiểm soát của bạn có thể không cung cấp bất kỳ lợi ích thực sự nào, và có thể không có cách nào có ý nghĩa để đối phó với ngoại lệ. Đối với một ví dụ trực tiếp, nếu một người đang triển khai mẫu Có thể quan sát được (bằng ngôn ngữ như C # nơi bạn có các sự kiện ở mọi nơi và không có throwsđịnh nghĩa rõ ràng trong định nghĩa), thì không có lý do thực tế nào để Người quan sát phá vỡ luồng điều khiển của bạn nếu nó gặp sự cố và không có cách nào có ý nghĩa để đối phó với những thứ của họ (tất nhiên, một người hàng xóm tốt không nên ném khi quan sát, nhưng không ai hoàn hảo).

Quan sát ở trên có thể được mở rộng cho bất kỳ giao diện ghép lỏng lẻo nào (như bạn đã chỉ ra); Tôi nghĩ rằng đó thực sự là một tiêu chuẩn mà sau khi leo lên 3-6 khung stack, một ngoại lệ chưa được phát hiện có khả năng kết thúc trong một phần của mã:

  • quá trừu tượng để đối phó với ngoại lệ theo bất kỳ cách có ý nghĩa nào, ngay cả khi chính ngoại lệ đó bị u ám;
  • đang thực hiện một chức năng chung (không thể quan tâm ít hơn về lý do tại sao bạn thất bại, chẳng hạn như bơm thông báo hoặc có thể quan sát được);
  • là cụ thể, nhưng với một trách nhiệm khác, và nó thực sự không nên lo lắng;

Xem xét ở trên, trang trí giao diện với throwsngữ nghĩa chỉ là một lợi ích chức năng cận biên, bởi vì rất nhiều người gọi thông qua hợp đồng giao diện sẽ chỉ quan tâm nếu bạn thất bại, không phải tại sao.

Tôi sẽ nói rằng nó trở thành một câu hỏi về hương vị và sự thuận tiện: trọng tâm chính của bạn là phục hồi một cách duyên dáng trạng thái của bạn trong cả người gọi và callee sau một "ngoại lệ", do đó, nếu bạn có nhiều kinh nghiệm trong việc di chuyển mã lỗi xung quanh (sắp tới từ nền C) hoặc nếu bạn làm việc trong môi trường ngoại lệ có thể trở thành ác quỷ (C ++), tôi không tin rằng việc ném đồ đạc xung quanh là rất quan trọng đối với OOP sạch đẹp mà bạn không thể dựa vào cũ mẫu nếu bạn không thoải mái với nó. Đặc biệt là nếu nó dẫn đến phá vỡ SoC.

Từ góc độ lý thuyết, tôi nghĩ rằng cách xử lý ngoại lệ của SoC-kosher có thể xuất phát trực tiếp từ quan sát rằng hầu hết các lần người gọi trực tiếp chỉ quan tâm rằng bạn thất bại, không phải tại sao. Callee ném, một người nào đó ở rất gần (2-3 khung hình) bắt được một phiên bản bị bỏ qua và ngoại lệ thực tế luôn bị chìm vào một trình xử lý lỗi chuyên dụng (ngay cả khi chỉ theo dõi) - đây là nơi AOP sẽ có ích, bởi vì những trình xử lý này có khả năng là ngang.


1

Ngoại lệ ủng hộ mã lỗi

  • Cả hai nên cùng tồn tại.

  • Trả lại mã lỗi khi bạn dự đoán hành vi nhất định.

  • Trả lại ngoại lệ khi bạn không lường trước một số hành vi.

  • Mã lỗi thường được liên kết với một tin nhắn, khi loại ngoại lệ vẫn còn, nhưng một tin nhắn có thể thay đổi

  • Ngoại lệ có dấu vết ngăn xếp, khi mã lỗi không. Tôi không sử dụng mã lỗi để gỡ lỗi hệ thống.

Mã hóa cho các giao diện không triển khai

Điều này có thể đặc trưng cho JAVA, nhưng khi tôi khai báo các giao diện của mình, tôi không chỉ định ngoại lệ nào có thể bị ném khi triển khai giao diện đó, điều đó không có nghĩa gì.

Khi chúng tôi bắt gặp một ngoại lệ chắc chắn chúng tôi sẽ đưa ra các giả định về việc thực hiện?

Điều này là hoàn toàn tùy thuộc vào bạn. Bạn có thể thử và bắt một loại ngoại lệ rất cụ thể và sau đó bắt một tổng quát hơn Exception. Tại sao không để ngoại lệ lan truyền lên ngăn xếp và sau đó xử lý nó? Ngoài ra, bạn có thể xem xét lập trình khía cạnh nơi xử lý ngoại lệ trở thành khía cạnh "có thể cắm được".

Điều gì xảy ra nếu việc triển khai không cần ném ngoại lệ hoặc cần ném ngoại lệ khác?

Tôi không hiểu tại sao nó là một vấn đề cho bạn. Có, bạn có thể có một triển khai không bao giờ thất bại hoặc ném ngoại lệ và bạn có thể có một triển khai khác liên tục thất bại và ném ngoại lệ. Nếu đó là trường hợp, thì đừng chỉ định bất kỳ trường hợp ngoại lệ nào trên giao diện và vấn đề của bạn đã được giải quyết.

Nó sẽ thay đổi bất cứ điều gì nếu thay vì ngoại lệ, việc triển khai của bạn trả về một đối tượng kết quả? Đối tượng này sẽ chứa kết quả hành động của bạn cùng với bất kỳ lỗi / lỗi nếu có. Sau đó bạn có thể thẩm vấn đối tượng đó.


1

Trừu tượng rò rỉ

Tại sao một giao diện nên chỉ định ngoại lệ nào có thể được ném? Điều gì xảy ra nếu việc triển khai không cần ném ngoại lệ hoặc cần ném ngoại lệ khác? Ở cấp độ giao diện, không có cách nào để biết ngoại lệ nào mà việc triển khai có thể muốn ném.

Theo kinh nghiệm của tôi, mã nhận được lỗi (có thể là ngoại lệ, mã lỗi hoặc bất cứ thứ gì khác) thường không quan tâm đến nguyên nhân chính xác của lỗi - nó sẽ phản ứng giống như bất kỳ lỗi nào, ngoại trừ báo cáo có thể về lỗi (có thể là hộp thoại báo lỗi hoặc một loại nhật ký nào đó); và báo cáo này sẽ được thực hiện trực giao với mã được gọi là thủ tục thất bại. Ví dụ: mã này có thể chuyển lỗi sang một số đoạn mã khác biết cách báo cáo các lỗi cụ thể (ví dụ: định dạng chuỗi thông báo), có thể đính kèm một số thông tin ngữ cảnh.

Tất nhiên, trong một số trường hợp nó cần thiết để gắn ngữ nghĩa cụ thể để lỗi và phản ứng khác nhau trên cơ sở đó lỗi xảy ra. Các trường hợp như vậy nên được ghi lại trong đặc tả giao diện. Tuy nhiên, giao diện vẫn có thể bảo lưu quyền đưa ra các ngoại lệ khác mà không có ý nghĩa cụ thể.


1

Tôi thấy rằng các ngoại lệ cho phép viết mã có cấu trúc và ngắn gọn hơn để báo cáo và xử lý lỗi: sử dụng mã lỗi yêu cầu kiểm tra giá trị trả về sau mỗi cuộc gọi và quyết định làm gì trong trường hợp có kết quả không mong muốn.

Mặt khác, tôi đồng ý rằng các trường hợp ngoại lệ tiết lộ chi tiết triển khai cần được ẩn trong mã gọi giao diện. Vì không thể biết một tiên nghiệm mà đoạn mã nào có thể đưa ra ngoại lệ nào (trừ khi chúng được khai báo trong chữ ký phương thức như trong Java), bằng cách sử dụng các ngoại lệ, chúng tôi đang đưa ra các phụ thuộc ngầm rất phức tạp giữa các phần khác nhau của mã, đó là chống lại nguyên tắc giảm thiểu phụ thuộc.

Tóm tắt:

  • Tôi nghĩ rằng các ngoại lệ cho phép một mã sạch hơn và một cách tiếp cận tích cực hơn để kiểm tra và gỡ lỗi vì các ngoại lệ chưa được nhìn thấy rõ hơn và khó bỏ qua hơn các mã lỗi (sớm thất bại).
  • Mặt khác, các lỗi ngoại lệ chưa được phát hiện mà không được phát hiện trong quá trình thử nghiệm có thể xuất hiện trong môi trường sản xuất dưới dạng sự cố. Trong một số trường hợp nhất định, hành vi này không được chấp nhận và trong trường hợp này tôi nghĩ sử dụng mã lỗi là một cách tiếp cận mạnh mẽ hơn.

1
Tôi không đồng ý. Có, các trường hợp ngoại lệ chưa được xử lý có thể làm sập một ứng dụng, nhưng các mã lỗi không được kiểm tra cũng có thể xảy ra - vì vậy đó là một lỗi. Nếu bạn sử dụng ngoại lệ đúng cách, những người chưa bị bắt sẽ là những người gây tử vong (như OutOfMemory), và đối với những trường hợp này, sụp đổ ngay lập tức là cách tốt nhất bạn có thể làm.
sleske

Mã lỗi là một phần của hợp đồng giữa người gọi m1 và callee m2: mã lỗi có thể được xác định chỉ trong giao diện của m2. Với các ngoại lệ (trừ khi bạn đang sử dụng Java và khai báo tất cả các ngoại lệ được ném trong chữ ký phương thức), bạn có một hợp đồng ngầm giữa người gọi m1 và tất cả các phương thức có thể được gọi bằng m2, theo cách đệ quy. Vì vậy, tất nhiên đó là một lỗi không kiểm tra mã lỗi được trả về, nhưng luôn luôn có thể làm điều đó. Mặt khác, không phải lúc nào cũng có thể kiểm tra tất cả các ngoại lệ được ném bởi một phương thức, trừ khi bạn biết cách nó được thực hiện.
Giorgio

Đầu tiên: Bạn có thể kiểm tra tất cả các trường hợp ngoại lệ - chỉ cần thực hiện "xử lý ngoại lệ Pokemon" (phải nắm bắt tất cả - tức là catch Exceptionhoặc thậm chí Throwablehoặc tương đương).
sleske

1
Trong thực tế, nếu API được thiết kế đúng, nó sẽ chỉ định tất cả các ngoại lệ mà khách hàng có thể xử lý một cách có ý nghĩa - những điều này cần phải được nắm bắt cụ thể. Đây là tương đương của mã lỗi). Bất kỳ ngoại lệ nào khác có nghĩa là "lỗi nội bộ" và ứng dụng sẽ cần phải tắt hoặc ít nhất là tắt hệ thống con tương ứng. Bạn có thể bắt những thứ này nếu bạn muốn (xem ở trên), nhưng bạn thường chỉ nên để chúng nổi bong bóng. "Sủi bọt" là lợi thế chính của việc sử dụng các ngoại lệ. Bạn vẫn có thể bắt chúng xa hơn, hoặc không, tùy theo nhu cầu.
sleske

-1

Phải thiên vị.

Nó không thể bị bỏ qua, nó phải được xử lý, nó hoàn toàn minh bạch. Và NẾU bạn sử dụng đúng loại lỗi tay trái, nó truyền tải tất cả thông tin giống như một ngoại lệ java.

Nhược điểm? Mã với xử lý lỗi thích hợp trông thật kinh tởm (đúng với tất cả các cơ chế).


điều này dường như không cung cấp bất cứ điều gì đáng kể qua các điểm được thực hiện và giải thích trong 10 câu trả lời trước. Tại sao lại trả lời câu hỏi hai năm tuổi với những thứ như thế
gnat

Ngoại trừ không ai ở đây đề cập đến sự thiên vị đúng. hugomg đã nói rất gần về haskell, tuy nhiên Có lẽ đó là một trình xử lý lỗi vì nó không giải thích được tại sao xảy ra lỗi, cũng không có phương pháp trực tiếp nào để phục hồi và gọi lại là một trong những tội lớn nhất trong thiết kế luồng điều khiển. Và câu hỏi này đã xuất hiện trên google.
Key Nam
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.