"Số lượng tốt" các ngoại lệ cần triển khai cho thư viện của tôi là gì?


20

Tôi đã luôn tự hỏi có bao nhiêu lớp ngoại lệ khác nhau mà tôi nên triển khai và ném cho các phần mềm khác nhau của mình. Sự phát triển cụ thể của tôi thường liên quan đến C ++ / C # / Java, nhưng tôi tin rằng đây là một câu hỏi cho tất cả các ngôn ngữ.

Tôi muốn hiểu thế nào là một số lượng lớn các trường hợp ngoại lệ khác nhau và cộng đồng nhà phát triển mong đợi gì về một thư viện tốt.

Sự đánh đổi mà tôi thấy bao gồm:

  • Các lớp ngoại lệ khác có thể cho phép xử lý lỗi ở mức rất tốt đối với người dùng API (dễ bị lỗi cấu hình hoặc dữ liệu của người dùng hoặc không tìm thấy tệp)
  • Nhiều lớp ngoại lệ cho phép thông tin cụ thể lỗi được nhúng trong ngoại lệ, thay vì chỉ một thông báo chuỗi hoặc mã lỗi
  • Nhiều lớp ngoại lệ có thể có nghĩa là bảo trì mã nhiều hơn
  • Nhiều lớp ngoại lệ hơn có thể có nghĩa là API ít tiếp cận hơn với người dùng

Các kịch bản tôi muốn hiểu cách sử dụng ngoại lệ bao gồm:

  • Trong giai đoạn 'cấu hình', có thể bao gồm tải tệp hoặc cài đặt tham số
  • Trong giai đoạn loại 'hoạt động' trong đó thư viện có thể đang chạy các tác vụ và thực hiện một số công việc, có lẽ trong một luồng khác

Các mẫu báo cáo lỗi khác mà không sử dụng ngoại lệ hoặc ít ngoại lệ hơn (để so sánh) có thể bao gồm:

  • Ít ngoại lệ hơn, nhưng nhúng mã lỗi có thể được sử dụng như một tra cứu
  • Trả lại mã lỗi và cờ trực tiếp từ các chức năng (đôi khi không thể thực hiện được từ các luồng)
  • Đã triển khai một sự kiện hoặc hệ thống gọi lại khi có lỗi (tránh việc bỏ xếp chồng)

Là nhà phát triển, bạn thích nhìn thấy gì?

Nếu có NHIỀU ngoại lệ, bạn có bận tâm xử lý lỗi riêng không?

Bạn có ưu tiên cho các loại xử lý lỗi tùy thuộc vào giai đoạn hoạt động không?



5
"API ít tiếp cận hơn với người dùng" - có thể được xử lý bằng cách có một số lớp ngoại lệ mà tất cả đều kế thừa từ một lớp cơ sở chung cho API. Sau đó, khi bạn bắt đầu, bạn có thể thấy API ngoại lệ đến từ đâu mà không phải lo lắng về tất cả các chi tiết. Vào thời điểm bạn hoàn thành chương trình đầu tiên của mình bằng API, sau đó nếu bạn cần thêm thông tin về lỗi thì bạn bắt đầu xem xét các trường hợp ngoại lệ khác thực sự có ý nghĩa gì. Tất nhiên, bạn đồng thời phải chơi độc đáo với hệ thống phân cấp ngoại lệ tiêu chuẩn của ngôn ngữ bạn đang sử dụng.
Steve Jessop

điểm thích hợp Steve
Fuzz

Câu trả lời:


18

Tôi giữ nó đơn giản.

Một thư viện có loại ngoại lệ cơ sở được mở rộng từ std ::: runtime_error (đó là từ C ++ áp dụng phù hợp với các ngôn ngữ khác). Ngoại lệ này có một chuỗi tin nhắn để chúng ta có thể đăng nhập; mỗi điểm ném có một thông điệp duy nhất (thường có một ID duy nhất).

Đó là về nó.

Lưu ý 1 : Trong trường hợp ai đó bắt ngoại lệ có thể khắc phục ngoại lệ và bắt đầu lại hành động. Tôi sẽ thêm các ngoại lệ xuất phát cho những thứ có khả năng được sửa chữa duy nhất tại một địa điểm từ xa. Nhưng điều này là rất rất hiếm (Hãy nhớ rằng người bắt không có khả năng ở gần điểm ném, do đó việc khắc phục vấn đề sẽ khó khăn (nhưng mọi thứ đều phụ thuộc vào tình huống)).

Lưu ý 2 : Đôi khi thư viện quá đơn giản nên không có giá trị ngoại lệ và std :: runtime_error sẽ làm. Điều quan trọng là chỉ có một ngoại lệ nếu khả năng phân biệt nó với std :: runtime_error có thể cung cấp cho người dùng đủ thông tin để làm điều gì đó với nó.

Lưu ý 3 : Trong một lớp tôi thường thích các mã lỗi (nhưng chúng sẽ không bao giờ thoát khỏi API công khai của lớp tôi).

Nhìn vào sự đánh đổi của bạn:

Sự đánh đổi mà tôi thấy bao gồm:

Các lớp ngoại lệ khác có thể cho phép xử lý lỗi ở mức rất tốt đối với người dùng API (dễ bị lỗi cấu hình hoặc dữ liệu của người dùng hoặc không tìm thấy tệp)

Có nhiều ngoại lệ thực sự cung cấp cho bạn kiểm soát hạt tốt hơn? Câu hỏi trở thành có thể mã bắt thực sự sửa lỗi dựa trên ngoại lệ. Tôi chắc chắn có những tình huống như vậy và trong những trường hợp này, bạn nên có một ngoại lệ khác. Nhưng tất cả các trường hợp ngoại lệ bạn đã liệt kê ở trên điều chỉnh hữu ích duy nhất là tạo ra một cảnh báo lớn và dừng ứng dụng.

Nhiều lớp ngoại lệ cho phép thông tin cụ thể lỗi được nhúng trong ngoại lệ, thay vì chỉ một thông báo chuỗi hoặc mã lỗi

Đây là lý do tuyệt vời để sử dụng ngoại lệ. Nhưng thông tin phải hữu ích cho người đang lưu trữ nó. Họ có thể sử dụng thông tin để thực hiện một số hành động khắc phục? Nếu đối tượng là nội bộ trong thư viện của bạn và không thể được sử dụng để ảnh hưởng đến bất kỳ API nào thì thông tin là vô ích. Bạn cần phải rất cụ thể rằng thông tin được ném có giá trị hữu ích cho người có thể nắm bắt nó. Người nắm bắt nó thường nằm ngoài API công khai của bạn, vì vậy điều chỉnh thông tin của bạn để nó có thể được sử dụng với những thứ trong API công khai của bạn.

Nếu tất cả những gì họ có thể làm là ghi nhật ký ngoại lệ thì tốt nhất chỉ nên ném một thông báo lỗi thay vì nhiều dữ liệu. Vì người bắt thường sẽ xây dựng một thông báo lỗi với dữ liệu. Nếu bạn xây dựng thông báo lỗi thì nó sẽ nhất quán trên tất cả các công cụ bắt, nếu bạn cho phép người bắt xây dựng thông báo lỗi, bạn có thể nhận được cùng một lỗi được báo cáo khác nhau tùy thuộc vào người đang gọi và bắt.

Ít ngoại lệ hơn, nhưng nhúng mã lỗi có thể được sử dụng như một tra cứu

Bạn phải xác định thời tiết mã lỗi có thể được sử dụng một cách có ý nghĩa. Nếu nó có thể thì bạn nên có ngoại lệ của riêng mình. Nếu không, người dùng của bạn bây giờ cần phải thực hiện các câu lệnh chuyển đổi bên trong bắt (điều này đánh bại toàn bộ vấn đề bắt tự động xử lý công cụ).

Nếu không thể thì tại sao không sử dụng một thông báo lỗi trong trường hợp ngoại lệ (không cần phải phân tách mã và thông báo khiến nó khó tìm kiếm).

Trả lại mã lỗi và cờ trực tiếp từ các chức năng (đôi khi không thể thực hiện được từ các luồng)

Trả lại mã lỗi là tuyệt vời trong nội bộ. Nó cho phép bạn sửa lỗi ở đó và sau đó và bạn phải chắc chắn rằng bạn đã sửa tất cả các mã lỗi và tài khoản cho chúng. Nhưng rò rỉ chúng trên API công khai của bạn là một ý tưởng tồi. Vấn đề là các lập trình viên thường quên kiểm tra các trạng thái lỗi (ít nhất là với một ngoại lệ, một lỗi không được kiểm soát sẽ buộc ứng dụng thoát khỏi một lỗi không được xử lý thường sẽ làm hỏng tất cả dữ liệu của bạn).

Đã triển khai một sự kiện hoặc hệ thống gọi lại khi có lỗi (tránh việc bỏ xếp chồng)

Phương pháp này thường được sử dụng cùng với các cơ chế xử lý lỗi khác (không phải là phương pháp thay thế). Hãy nghĩ về chương trình windows của bạn. Một người dùng bắt đầu một hành động bằng cách chọn một mục menu. Điều này tạo ra một hành động trên hàng đợi sự kiện. Hàng đợi sự kiện cuối cùng chỉ định một luồng để xử lý hành động. Các luồng được cho là để xử lý các hành động và cuối cùng trở lại nhóm luồng và chờ đợi một nhiệm vụ khác. Ở đây một ngoại lệ phải được bắt tại cơ sở bởi luồng được giao nhiệm vụ. Kết quả của việc bắt ngoại lệ thường sẽ dẫn đến một sự kiện được tạo cho vòng lặp chính, cuối cùng sẽ dẫn đến thông báo lỗi được hiển thị cho người dùng.

Nhưng trừ khi bạn có thể tiếp tục đối mặt với ngoại lệ, ngăn xếp sẽ được thư giãn (ít nhất là cho chủ đề).


+1; Tho "Nếu không, người dùng của bạn bây giờ cần phải thực hiện các câu lệnh chuyển đổi bên trong đó (điều này đánh bại toàn bộ vấn đề bắt tự động xử lý công cụ)." - Bỏ ngăn xếp cuộc gọi (nếu bạn vẫn có thể sử dụng nó) và xử lý lỗi bắt buộc vẫn là những lợi ích tuyệt vời. Nhưng nó chắc chắn sẽ làm mất khả năng thực hiện ngăn xếp cuộc gọi khi bạn phải bắt nó ở giữa và nghĩ lại.
Merlyn Morgan-Graham

9

Tôi thường bắt đầu với:

  1. Một lớp ngoại lệ cho các lỗi đối số . Ví dụ: "đối số không được phép là null", "đối số phải là số dương", v.v. Java và C # có các lớp được xác định trước cho các lớp đó; trong C ++ tôi thường chỉ tạo một lớp, xuất phát từ std :: ngoại lệ.
  2. Một lớp ngoại lệ cho lỗi tiền điều kiện . Những bài kiểm tra phức tạp hơn, như "chỉ số phải nhỏ hơn kích thước".
  3. Một lớp ngoại lệ cho lỗi xác nhận . Đó là để kiểm tra trạng thái cho các phương pháp nửa chừng. Ví dụ: khi lặp qua một danh sách để đếm các phần tử âm, bằng 0 hoặc dương, ở cuối ba phần tử đó sẽ cộng với kích thước.
  4. Một lớp ngoại lệ cơ sở cho chính thư viện. Lúc đầu, chỉ cần ném lớp này. Chỉ khi có nhu cầu, hãy bắt đầu thêm các lớp con.
  5. Tôi không thích bọc ngoại lệ, nhưng tôi biết rằng ý kiến ​​khác nhau ở điểm này (và rất tương quan với ngôn ngữ được sử dụng). Nếu bạn thực hiện bọc, bạn sẽ cần các lớp ngoại lệ gói bổ sung .

Vì các lớp trong 3 trường hợp đầu tiên là hỗ trợ gỡ lỗi, chúng không có ý định xử lý mã. Thay vào đó, chúng chỉ được bắt bởi trình xử lý cấp cao nhất hiển thị thông tin để người dùng có thể sao chép-dán nó cho nhà phát triển (hoặc thậm chí tốt hơn: nhấn nút "gửi báo cáo"). Vì vậy, bao gồm thông tin hữu ích cho nhà phát triển: tệp, chức năng, số dòng và một số thông báo xác định rõ ràng kiểm tra nào không thành công.

Vì 3 trường hợp đầu tiên giống nhau cho từng dự án, trong C ++ tôi thường chỉ sao chép chúng từ dự án trước. Vì nhiều người đang làm giống hệt nhau, các nhà thiết kế của C # và Java đã thêm các lớp tiêu chuẩn cho các trường hợp đó vào thư viện chuẩn. [CẬP NHẬT:] Đối với các lập trình viên lười biếng: một lớp có thể là đủ và với một số may mắn, thư viện tiêu chuẩn của bạn đã có một lớp ngoại lệ phù hợp. Tôi thích thêm thông tin như tên tệp và vải lanh, mà các lớp mặc định trong C ++ không cung cấp. [Kết thúc cập nhật]

Tùy thuộc vào thư viện, trường hợp thứ tư chỉ có thể có một lớp hoặc có thể trở thành một lớp đầy đủ. Tôi thích aproach nhanh nhẹn để bắt đầu đơn giản, thêm các lớp con khi có nhu cầu.

Để tranh luận chi tiết về trường hợp thứ tư của tôi, hãy xem câu trả lời của Loki Astari . Tôi hoàn toàn đồng ý với câu trả lời chi tiết của anh ấy.


+1; Có một sự khác biệt nhất định giữa cách bạn nên viết ngoại lệ viết trong một câu thơ khung làm thế nào bạn nên viết ngoại lệ trong một ứng dụng. Sự khác biệt là một chút mờ nhạt, nhưng bạn có đề cập đến nó (nói đến Loki cho trường hợp thư viện).
Merlyn Morgan-Graham
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.