2008-12-02 39 views
25

Tôi đã lập trình được 3 năm qua. Khi tôi lập trình, tôi sử dụng để xử lý tất cả các ngoại lệ đã biết và cảnh báo người dùng một cách duyên dáng. Tôi đã thấy một số mã gần đây trong đó có gần như tất cả các phương pháp được bọc bên trong khối try/catch. Tác giả nói rằng nó là một phần của chương trình phòng thủ. Tôi tự hỏi, đây có phải là chương trình phòng thủ thực sự không? Bạn có đề nghị đặt tất cả mã của bạn trong các khối thử không?Không gói tất cả mọi thứ trong khối try/catch tạo thành lập trình phòng thủ?

+6

Không chắc chắn về điều đó. Các câu hỏi, trong khi có tiêu đề chung, thực sự là khá cụ thể về việc sử dụng trường hợp ngoại lệ như một phương pháp lập trình phòng thủ, và những gì là những mối quan tâm với làm như vậy. – Elie

Trả lời

52

Quy tắc cơ bản của tôi là: Trừ khi bạn có thể sửa lỗi vấn đề gây ra ngoại lệ, không bắt được nó, hãy để bong bóng đến mức có thể giải quyết được.

Theo kinh nghiệm của tôi, 95% tất cả các khối bắt hoặc chỉ bỏ qua ngoại lệ (catch {}) hoặc chỉ ghi nhật ký lỗi và sửa lại ngoại lệ. Sau này có vẻ như là điều đúng đắn để làm, nhưng trong thực tế, khi điều này được thực hiện ở mọi cấp độ, bạn chỉ đơn thuần là kết thúc với nhật ký của bạn lộn xộn với năm bản sao của cùng một thông báo lỗi. Thông thường, các ứng dụng này có "bỏ qua bắt" ở cấp cao nhất (vì "chúng tôi đã thử/nắm bắt ở tất cả các cấp thấp hơn"), dẫn đến ứng dụng rất chậm với nhiều trường hợp ngoại lệ bị nhỡ và nhật ký lỗi quá dài bất cứ ai sẵn sàng nhìn qua nó.

+7

Chính xác. Đặt mọi thứ vào một câu lệnh try/catch thường là dấu hiệu của một nhà phát triển thiếu kinh nghiệm, những người không biết nhiều về ngoại lệ IME. –

+0

Chúng ta nên được phép mod + 2 nếu chúng ta dành điểm đại diện như downmodding. Nhưng tôi sẽ làm điều đó ở đây. –

+2

Tôi nghĩ rằng đôi khi, đặc biệt khi vượt qua ranh giới của một mô-đun hoặc thư viện, thực hành tốt là nắm bắt ngoại lệ, bọc nó trong một loại ngoại lệ khác (như các cơ sở nguyên nhân của Java) và quay lại. – rmeador

5

Có một điều như quá trình xử lý "quá nhiều" và bắt tất cả các ngoại lệ sẽ đánh bại điểm. Cụ thể cho C++, câu lệnh catch (...) bắt tất cả ngoại lệ nhưng bạn không thể xử lý nội dung của ngoại lệ đó vì bạn không biết loại ngoại lệ (và nó có thể là bất kỳ thứ gì).

Bạn nên nắm bắt các ngoại lệ mà bạn có thể xử lý toàn bộ hoặc một phần, tính lại các ngoại lệ một phần. Bạn không nên bắt bất kỳ ngoại lệ nào mà bạn không thể xử lý, bởi vì điều đó sẽ chỉ làm xáo trộn các lỗi có thể (hoặc đúng hơn, sẽ) cắn bạn sau này.

3

Tôi đoán câu trả lời thực sự là "Phụ thuộc". Nếu các khối try-catch đang bắt các ngoại lệ rất chung chung thì tôi sẽ nói đó là lập trình phòng thủ theo cùng một cách mà không bao giờ lái xe ra khỏi khu phố của bạn là lái xe phòng thủ. Một try-catch (imo) nên được điều chỉnh theo các ngoại lệ cụ thể.

Một lần nữa, đây chỉ là ý kiến ​​của tôi, nhưng khái niệm lập trình phòng thủ của tôi là bạn cần ít hơn/nhỏ hơn khối try-catch không nhiều hơn/lớn hơn. Mã của bạn nên làm mọi thứ có thể để đảm bảo rằng một điều kiện ngoại lệ không bao giờ có thể tồn tại ngay từ đầu.

4

Tôi khuyên bạn nên chống lại thực tiễn này. Đưa mã vào các khối try-catch khi bạn biết các loại ngoại lệ có thể bị ném là một chuyện. Nó cho phép bạn, như bạn đã nói, để khôi phục một cách duyên dáng và/hoặc cảnh báo người dùng về lỗi. Tuy nhiên, đặt tất cả các mã của bạn bên trong các khối như vậy, nơi bạn không biết lỗi nào có thể xảy ra là sử dụng các ngoại lệ để kiểm soát luồng chương trình, điều này là không lớn.

Nếu bạn đang viết mã có cấu trúc tốt, bạn sẽ biết về mọi ngoại lệ có thể xảy ra và có thể nắm bắt được những ngoại lệ đó. Nếu bạn không biết làm thế nào một ngoại lệ cụ thể có thể được ném, sau đó không bắt nó, chỉ trong trường hợp. Khi nó xảy ra, sau đó bạn có thể tìm ra ngoại lệ, những gì gây ra nó, và sau đó bắt nó.

8

Ghi ngoại lệ ngẫu nhiên là xấu. Vậy thì sao?

  • Bỏ qua chúng? Xuất sắc. Hãy cho tôi biết làm thế nào mà làm việc cho họ.
  • Đăng nhập và tiếp tục chạy? Tôi nghĩ là không.
  • Ném một ngoại lệ khác như một phần của sự cố? Chúc may mắn gỡ lỗi đó.

Ghi ngoại lệ mà bạn thực sự có thể làm điều gì đó có ý nghĩa là tốt. Những trường hợp này dễ xác định và duy trì.

+0

Khó gỡ lỗi gì về # 3? –

+0

@Bart van Heukelom: Ngoại lệ khác mà mặt nạ ngoại lệ ban đầu làm cho các bản ghi khó hiểu. Thông điệp tường trình là "SomeRandomException". Nguyên nhân gốc là ValueError. Hơn nữa, các traceback ban đầu có thể dễ dàng bị lạc trong muddle của nâng cao một ngoại lệ mới. Thử nó. –

+0

Đó là những gì gây ra chuỗi Java gây ra. Tôi giả định các ngôn ngữ khác có điều này quá (tôi biết ngay cả PHP 5.3 không). –

10

Thuật ngữ "lập trình phòng thủ" là viết tắt của mã viết theo cách sao cho nó có thể khôi phục từ các tình huống lỗi hoặc tránh tình trạng lỗi hoàn toàn. Ví dụ:

private String name; 

public void setName(String name) { 
} 

Bạn xử lý tên == null như thế nào? Bạn có ném một ngoại lệ hay bạn chấp nhận nó? Nếu nó không có ý nghĩa để có một đối tượng không có tên, thì bạn nên ném một ngoại lệ. Còn tên == "" thì sao?

Nhưng ... sau đó bạn viết trình chỉnh sửa. Trong khi bạn thiết lập giao diện người dùng, bạn thấy rằng có những tình huống mà người dùng có thể quyết định lấy tên hoặc tên có thể bị trống trong khi người dùng chỉnh sửa nó.

Một ví dụ khác:

public boolean isXXX (String s) { 
} 

Ở đây, chiến lược phòng thủ thường là để trả về false null khi s == (tránh NPEs khi bạn có thể).

Hoặc:

public String getName() { 
} 

Một lập trình phòng thủ có thể trở về "" nếu tên == null để tránh NPEs kêu gọi mã.

6

Tôi có thể nói một điều ở đây là mỗi khi một trong những đồng nghiệp của tôi viết chữ ký phương thức với "ném ngoại lệ" thay vì liệt kê các loại ngoại lệ mà phương pháp thực sự ném, tôi muốn đi qua và bắn chúng trong đầu? Vấn đề là sau một thời gian bạn đã có 14 cấp độ của tất cả các cuộc gọi nói rằng "ném ngoại lệ", do đó, tái cấu trúc để làm cho họ tuyên bố những gì họ thực sự ném là một bài tập lớn.

+0

Đồng ý hết lòng! Ẩn dụ, tất nhiên. Không thực sự ủng hộ việc bắn bất cứ ai trong bất kỳ phần nào của người của họ. –

+0

Chỉ cần viết một phương thức gọi là shootProgrammer (Programmer p) ném OutOfAmmoException – Elie

+2

@Bill - là nó ok nếu tôi smack chúng với một tờ báo cuộn lên sau đó? –

9

Nếu bạn đang đi để xử lý ngoại lệ ngẫu nhiên, xử lý chúng trong chỉ một nơi - trên đỉnh của các ứng dụng, nhằm mục đích:

  • trình bày một thông điệp thân thiện với người dùng, và
  • lưu chẩn đoán.

Đối với mọi thứ khác, bạn muốn có sự cố ngay lập tức, vị trí cụ thể nhất, để bạn nắm bắt những điều này càng sớm càng tốt - ngoại trừ việc xử lý ngoại lệ trở thành cách ẩn thiết kế và mã độc.

Trong hầu hết các trường hợp ngoại lệ có thể dự đoán được, có thể kiểm tra trước thời hạn, với điều kiện mà trình xử lý ngoại lệ sẽ bắt.

Nói chung, Nếu ... Khác thì tốt hơn nhiều so với Thử ... Bắt.

+0

Tôi không đồng ý. Có những ngoại lệ mà bạn xử lý theo cách đó, nhưng có những ngoại lệ bạn xử lý sâu - ví dụ, một số mã của tôi ném một "ScheduleConflictException", và một vài cấp cao hơn có mã để thay đổi lịch biểu và thử lại. –

+0

@Paul, "Trong hầu hết các trường hợp", và tôi nghĩ ngoại lệ cá nhân là ngoại lệ. Trong trường hợp này, bạn đang tạo một ngoại lệ để xử lý cao hơn, và tôi không thấy điều này như những gì cris5gd đang xem xét như một trường hợp chung. –

+1

@Paul Và điều này nghe có vẻ như không phải là một ngoại lệ ... –

14

Không, đó không phải là "lập trình phòng thủ". Đồng nghiệp của bạn đang cố gắng hợp lý hoá thói quen xấu của mình bằng cách sử dụng một từ thông dụng cho một thói quen tốt.

Điều anh ấy đang làm nên được gọi là "quét nó dưới tấm thảm". Nó giống như thống nhất (void) -giá trị trả về trạng thái lỗi từ các cuộc gọi phương thức.

+1

Tôi có cho thấy tuổi của tôi bằng cách nói rằng tôi ngay lập tức nhắc nhở in printf "không phải là một máy đánh chữ" không có vấn đề gì errno được? –

18

Sử dụng rộng rãi Thử ... Bắt không phải là lập trình phòng thủ, chỉ cần đóng đinh xác chết ở vị trí thẳng đứng.

Thử ... Cuối cùng có thể được sử dụng rộng rãi để phục hồi khi đối mặt với các ngoại lệ không mong muốn. Chỉ khi bạn mong đợi một ngoại lệ và bây giờ làm thế nào để đối phó với nó, bạn nên sử dụng Try..Catch thay thế.

Thỉnh thoảng tôi thấy Try..Catch System.Exception, trong đó khối catch chỉ ghi lại ngoại lệ và quay lại. Có ít nhất 3 vấn đề với cách tiếp cận đó:

  • Trả lại giả định một ngoại lệ chưa được giải quyết và do đó chương trình sẽ chấm dứt vì nó ở trạng thái không xác định. Nhưng bắt gây ra các khối cuối cùng bên dưới khối catch để chạy. Trong một tình huống không xác định, mã trong các khối Cuối cùng này có thể làm cho vấn đề tồi tệ hơn.
  • Mã trong những khối Cuối cùng này sẽ thay đổi trạng thái chương trình. Vì vậy, bất kỳ đăng nhập nào sẽ không nắm bắt trạng thái chương trình thực tế khi ngoại lệ ban đầu được ném. Và việc điều tra sẽ khó khăn hơn bởi vì nhà nước đã thay đổi.
  • Nó mang lại cho bạn trải nghiệm gỡ lỗi khốn khổ, bởi vì trình gỡ lỗi dừng lại trên sự phục hồi, không phải là lần ném gốc.
+5

+1 cho 'đánh bóng xác chết' – ChrisA

2

Tôi đã tìm thấy "cố gắng" "bắt" các khối rất hữu ích, đặc biệt nếu bất kỳ thời gian thực nào (chẳng hạn như truy cập cơ sở dữ liệu) được sử dụng.

Quá nhiều? Mắt của kẻ si tình.

Tôi thấy rằng sao chép nhật ký vào Word và tìm kiếm bằng "tìm" - nếu trình đọc nhật ký không có "tìm" hoặc "tìm kiếm" như một phần của công cụ được bao gồm - đơn giản nhưng tuyệt vời cách đăng nhập thông qua nhật ký chi tiết.

Chắc chắn có vẻ như "phòng thủ" theo nghĩa thông thường của từ đó.

Tôi đã tìm thấy, thông qua trải nghiệm, để theo dõi bất kỳ điều gì người quản lý, trưởng nhóm hoặc đồng nghiệp của bạn làm. Nếu bạn chỉ đang lập trình cho chính mình, hãy sử dụng chúng cho đến khi mã "ổn định" hoặc trong các bản dựng lỗi và sau đó xóa chúng khi hoàn tất.

4

Trong C++, một lý do để viết nhiều khối try/catch là lấy dấu vết ngăn xếp của trường hợp ngoại lệ được ném. Những gì bạn làm là viết một try/catch ở khắp mọi nơi, và (giả sử bạn không ở đúng chỗ để đối phó với ngoại lệ) có log catch một số thông tin dấu vết sau đó lại ném ngoại lệ. Bằng cách này, nếu một ngoại lệ bong bóng tất cả các con đường lên và gây ra chương trình chấm dứt, bạn sẽ có một dấu vết ngăn xếp đầy đủ của nơi mà tất cả bắt đầu đi sai (nếu bạn không làm điều này, sau đó một ngoại lệ C++ unhandled sẽ đã giúp bạn tháo gỡ ngăn xếp và loại bỏ bất kỳ khả năng nào của bạn để tìm ra nó xuất phát từ đâu).

Tôi sẽ tưởng tượng rằng trong bất kỳ ngôn ngữ nào có xử lý ngoại lệ tốt hơn (nghĩa là ngoại lệ không cho bạn biết chúng đến từ đâu), bạn chỉ muốn bắt ngoại lệ nếu bạn có thể làm gì đó cho chúng. Nếu không, bạn chỉ làm cho chương trình của bạn khó đọc.

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