Thiết kế bằng hợp đồng sử dụng các xác nhận hoặc ngoại lệ? [đóng cửa]


123

Khi lập trình bằng hợp đồng, một chức năng hoặc phương thức trước tiên sẽ kiểm tra xem các điều kiện tiên quyết của nó có được thực hiện hay không, trước khi bắt đầu thực hiện các trách nhiệm của nó, phải không? Hai cách nổi bật nhất để làm những kiểm tra là bởi assertvà bằng exception.

  1. khẳng định chỉ thất bại trong chế độ gỡ lỗi. Để đảm bảo rằng điều quan trọng là (đơn vị) kiểm tra tất cả các điều kiện tiên quyết của hợp đồng để xem liệu chúng có thực sự thất bại hay không.
  2. ngoại lệ không thành công trong chế độ gỡ lỗi và phát hành. Điều này có lợi ích là hành vi gỡ lỗi được kiểm tra giống hệt với hành vi phát hành, nhưng nó phải chịu một hình phạt hiệu năng thời gian chạy.

Cái nào bạn nghĩ là thích hợp hơn?

Xem câu hỏi liên quan ở đây


3
Toàn bộ quan điểm đằng sau thiết kế theo hợp đồng là bạn không cần (và có thể không nên) xác minh các điều kiện tiên quyết trong thời gian chạy. Bạn kiểm tra đầu vào trước khi đi qua nó vào phương pháp này với các điều kiện tiên quyết, đó là cách bạn tôn trọng của bạn kết thúc hợp đồng. Nếu đầu vào không hợp lệ hoặc vi phạm kết thúc hợp đồng của bạn, chương trình thường sẽ thất bại thông qua quá trình hành động thông thường của nó (mà bạn muốn).
void.pulum

Câu hỏi hay, nhưng tôi nghĩ bạn thực sự nên chuyển câu trả lời được chấp nhận (như các phiếu bầu cũng vậy)!
DaveFar

Mãi về sau, tôi mới biết, nhưng câu hỏi này có thực sự có thẻ c ++ không? Tôi đang tìm kiếm câu trả lời này, để sử dụng trong một ngôn ngữ khác (Delpih) và tôi không thể tưởng tượng bất kỳ ngôn ngữ nào có tính năng ngoại lệ và khẳng định sẽ không tuân theo các quy tắc tương tự. (Vẫn đang học hướng dẫn về Stack Overflow.)
Eric G

Phản hồi rất ngắn gọn được đưa ra trong phản hồi này : "Nói cách khác, các trường hợp ngoại lệ giải quyết sự mạnh mẽ của ứng dụng của bạn trong khi các xác nhận giải quyết tính chính xác của nó."
Shmuel Levine

Câu trả lời:


39

Vô hiệu hóa khẳng định trong các bản dựng phát hành cũng giống như nói "Tôi sẽ không bao giờ có bất kỳ vấn đề nào trong bản dựng phát hành", điều này thường không xảy ra. Vì vậy, khẳng định không nên bị vô hiệu hóa trong một bản dựng phát hành. Nhưng bạn không muốn bản dựng phát hành bị sập bất cứ khi nào xảy ra lỗi, phải không?

Vì vậy, sử dụng ngoại lệ và sử dụng chúng tốt. Sử dụng một hệ thống phân cấp ngoại lệ tốt, vững chắc và đảm bảo rằng bạn nắm bắt được và bạn có thể đặt một cái móc vào ngoại lệ ném vào trình gỡ lỗi của bạn để bắt nó, và trong chế độ phát hành, bạn có thể bù đắp lỗi thay vì sự cố thẳng. Đó là cách an toàn hơn để đi.


4
Các xác nhận rất hữu ích trong trường hợp kiểm tra tính đúng đắn là không hiệu quả hoặc không hiệu quả để thực hiện đúng.
Casebash

89
Điểm trong các xác nhận không phải là sửa lỗi, mà là để cảnh báo cho lập trình viên. Giữ cho chúng được kích hoạt trong phiên bản xây dựng là vô ích cho rằng lý do: Những gì bạn sẽ đạt được bằng cách có một bắn khẳng định? Nhà phát triển sẽ không thể nhảy vào và gỡ lỗi nó. Các xác nhận là một trợ giúp gỡ lỗi, chúng không phải là sự thay thế cho các trường hợp ngoại lệ (và cũng không phải là ngoại lệ thay thế cho các xác nhận). Ngoại lệ cảnh báo chương trình đến một điều kiện lỗi. Khẳng định cảnh báo cho nhà phát triển.
jalf

12
Nhưng một xác nhận nên được sử dụng khi dữ liệu nội bộ bị hỏng trong quá trình sửa lỗi - nếu một xác nhận kích hoạt, bạn không thể đưa ra giả định nào về trạng thái của chương trình vì điều đó có nghĩa là có gì đó / sai /. Nếu một xác nhận đã tắt, bạn không thể cho rằng bất kỳ dữ liệu nào là hợp lệ. Đó là lý do tại sao một bản phát hành nên khẳng định - không cho người lập trình biết vấn đề là ở đâu, nhưng để chương trình có thể tắt và không gặp rủi ro lớn hơn. Chương trình chỉ nên làm những gì có thể để tạo điều kiện phục hồi sau này, khi dữ liệu có thể được tin cậy.
coppro

5
@jalf, Mặc dù bạn không thể đặt một cái móc vào trình gỡ lỗi của mình trong các bản dựng phát hành, bạn có thể tận dụng ghi nhật ký để các nhà phát triển thấy thông tin liên quan đến xác nhận của bạn không thành công. Trong tài liệu này ( martinfowler.com/ieeeSoftware/failFast.pdf ), Jim Shore chỉ ra rằng: "Hãy nhớ rằng, một lỗi xảy ra tại trang web của khách hàng đã xảy ra trong quá trình thử nghiệm của bạn. Bạn có thể gặp khó khăn khi sao chép nó. khó tìm nhất và một khẳng định đúng đắn giải thích vấn đề có thể giúp bạn tiết kiệm nhiều ngày nỗ lực. "
StriplingWar Warrior

5
Cá nhân tôi thích khẳng định cho thiết kế bằng cách tiếp cận hợp đồng. Các ngoại lệ là phòng thủ và đang thực hiện kiểm tra đối số bên trong hàm. Ngoài ra, các điều kiện tiên quyết của dbc không nói "Tôi sẽ không hoạt động nếu bạn sử dụng các giá trị ngoài phạm vi hoạt động" nhưng "Tôi sẽ không đảm bảo cung cấp câu trả lời đúng, nhưng tôi vẫn có thể làm". Các xác nhận cung cấp cho nhà phát triển thông tin phản hồi rằng họ đang gọi một chức năng có vi phạm điều kiện, nhưng đừng ngăn họ sử dụng nếu họ cảm thấy họ biết rõ hơn. Vi phạm có thể gây ra ngoại lệ xảy ra, nhưng tôi thấy đó là một điều khác.
Matt_JD

194

Nguyên tắc chung là bạn nên sử dụng các xác nhận khi bạn đang cố gắng bắt lỗi của mình và ngoại lệ khi cố bắt lỗi của người khác. Nói cách khác, bạn nên sử dụng các ngoại lệ để kiểm tra các điều kiện tiên quyết cho các hàm API công khai và bất cứ khi nào bạn nhận được bất kỳ dữ liệu nào bên ngoài hệ thống của mình. Bạn nên sử dụng các xác nhận cho các chức năng hoặc dữ liệu bên trong hệ thống của bạn.


Điều gì về serialization / deserialize ngồi trong các mô-đun / ứng dụng khác nhau và cuối cùng không đồng bộ? Ý tôi là về phần người đọc, đó luôn là lỗi của tôi nếu tôi cố đọc sai mọi thứ vì vậy tôi có xu hướng sử dụng các xác nhận, nhưng mặt khác tôi có dữ liệu ngoài, cuối cùng có thể thay đổi định dạng mà không cần thông báo trước.
Slava

Nếu dữ liệu là bên ngoài, thì bạn nên sử dụng ngoại lệ. Trong trường hợp cụ thể này, có lẽ bạn cũng nên nắm bắt những ngoại lệ đó và xử lý chúng theo cách hợp lý, thay vì chỉ để chương trình của bạn chết. Ngoài ra, câu trả lời của tôi là một quy tắc của ngón tay cái, không phải là quy luật tự nhiên. :) Vì vậy, bạn phải xem xét từng trường hợp cá nhân.
Dima

Nếu hàm f (int * x) của bạn chứa một dòng x-> len trong đó, thì f (v) trong đó v được chứng minh là null được đảm bảo bị sập. Hơn nữa, nếu thậm chí sớm hơn trên v được chứng minh là null nhưng f (v) được chứng minh là được gọi, bạn có mâu thuẫn logic. Nó giống như có a / b trong đó b cuối cùng được chứng minh là 0. Lý tưởng nhất là mã đó không thể biên dịch. Tắt kiểm tra giả định là hoàn toàn ngu ngốc trừ khi vấn đề là chi phí kiểm tra, bởi vì nó che khuất vị trí nơi một giả định bị vi phạm. Nó ít nhất phải được đăng nhập. Dù sao thì bạn cũng nên có một thiết kế khởi động lại khi gặp sự cố.
Cướp

22

Nguyên tắc tôi tuân theo là: Nếu một tình huống có thể tránh được thực tế bằng cách mã hóa thì hãy sử dụng một xác nhận. Nếu không thì sử dụng một ngoại lệ.

Các xác nhận là để đảm bảo rằng Hợp đồng đang được tuân thủ. Hợp đồng phải công bằng, để khách hàng phải ở trong một vị trí để đảm bảo nó tuân thủ. Ví dụ: bạn có thể nêu trong hợp đồng rằng URL phải hợp lệ vì các quy tắc về URL là gì và không phải là URL hợp lệ được biết và nhất quán.

Các ngoại lệ dành cho các tình huống nằm ngoài sự kiểm soát của cả máy khách và máy chủ. Một ngoại lệ có nghĩa là một cái gì đó đã đi sai, và không có gì có thể được thực hiện để tránh nó. Ví dụ: kết nối mạng nằm ngoài sự kiểm soát của ứng dụng, do đó không thể làm gì để tránh lỗi mạng.

Tôi muốn thêm rằng phân biệt Xác nhận / Ngoại lệ không thực sự là cách tốt nhất để suy nghĩ về nó. Điều bạn thực sự muốn nghĩ đến là hợp đồng và cách thức thực thi. Trong ví dụ URL của tôi ở trên, điều tốt nhất cần làm là có một lớp đóng gói một URL và là Null hoặc một URL hợp lệ. Đó là việc chuyển đổi một chuỗi thành một URL để thực thi hợp đồng và một ngoại lệ được đưa ra nếu nó không hợp lệ. Một phương thức có tham số URL rõ ràng hơn nhiều so với phương thức có tham số Chuỗi và xác nhận chỉ định URL.


6

Các xác nhận là để bắt một cái gì đó mà một nhà phát triển đã làm sai (không chỉ bản thân bạn - một nhà phát triển khác trong nhóm của bạn cũng vậy). Nếu nó hợp lý rằng một lỗi người dùng có thể tạo ra tình trạng này, thì đó phải là một ngoại lệ.

Tương tự như vậy nghĩ về hậu quả. Một khẳng định thường tắt ứng dụng. Nếu có bất kỳ kỳ vọng thực tế nào mà tình trạng có thể được phục hồi, có lẽ bạn nên sử dụng một ngoại lệ.

Mặt khác, nếu vấn đề chỉ có thể là do lỗi lập trình viên thì hãy sử dụng một xác nhận, bởi vì bạn muốn biết về nó càng sớm càng tốt. Một ngoại lệ có thể bị bắt và xử lý, và bạn sẽ không bao giờ tìm hiểu về nó. Và có, bạn nên vô hiệu hóa các xác nhận trong mã phát hành vì ở đó bạn muốn ứng dụng phục hồi nếu có cơ hội nhỏ nhất có thể. Ngay cả khi trạng thái chương trình của bạn bị phá vỡ nghiêm trọng, người dùng vẫn có thể lưu công việc của họ.


5

Điều đó không chính xác là "khẳng định chỉ thất bại trong chế độ gỡ lỗi."

Trong Xây dựng phần mềm hướng đối tượng, Ấn bản thứ 2 của Bertrand Meyer, tác giả để một cánh cửa mở để kiểm tra các điều kiện tiên quyết trong chế độ phát hành. Trong trường hợp đó, điều xảy ra khi một xác nhận thất bại là ... một ngoại lệ vi phạm khẳng định được nêu ra! Trong trường hợp này, không có sự phục hồi từ tình huống: mặc dù điều gì đó hữu ích có thể được thực hiện và đó là tự động tạo một báo cáo lỗi và, trong một số trường hợp, để khởi động lại ứng dụng.

Động lực đằng sau điều này là các điều kiện tiên quyết thường rẻ hơn để kiểm tra so với bất biến và hậu điều kiện, và trong một số trường hợp tính chính xác và "an toàn" trong bản dựng phát hành quan trọng hơn tốc độ. tức là đối với nhiều ứng dụng, tốc độ không phải là vấn đề, nhưng sự mạnh mẽ (khả năng của chương trình hành xử một cách an toàn khi hành vi của nó không đúng, tức là khi hợp đồng bị phá vỡ).

Bạn nên luôn luôn để kiểm tra điều kiện tiên quyết được kích hoạt? Nó phụ thuộc. Tuỳ bạn. Không có câu trả lời phổ quát. Nếu bạn đang tạo phần mềm cho ngân hàng, có thể tốt hơn là làm gián đoạn thực thi bằng một thông điệp đáng báo động hơn là chuyển 1.000.000 đô la thay vì 1.000 đô la. Nhưng nếu bạn đang lập trình một trò chơi thì sao? Có thể bạn cần tất cả tốc độ bạn có thể nhận được và nếu ai đó nhận được 1000 điểm thay vì 10 vì một lỗi mà điều kiện tiên quyết không bắt được (vì chúng không được kích hoạt), thật may mắn.

Trong cả hai trường hợp, lý tưởng nhất là bạn đã bắt được lỗi đó trong quá trình thử nghiệm và bạn nên thực hiện một phần quan trọng trong thử nghiệm của mình với các xác nhận được bật. Điều đang được thảo luận ở đây là chính sách tốt nhất cho những trường hợp hiếm hoi trong đó các điều kiện tiên quyết thất bại trong mã sản xuất trong một kịch bản không được phát hiện trước đó do thử nghiệm không đầy đủ.

Để tóm tắt, bạn có thể có các xác nhận và vẫn tự động nhận các ngoại lệ , nếu bạn bật chúng - ít nhất là ở Eiffel. Tôi nghĩ để làm điều tương tự trong C ++, bạn cần phải tự gõ nó.

Xem thêm: Khi nào các xác nhận nên ở trong mã sản xuất?


1
Quan điểm của bạn chắc chắn là hợp lệ. SO không chỉ định một ngôn ngữ cụ thể - trong trường hợp của C # tiêu chuẩn khẳng định là System.Diagnostics.Debug.Assert, mà không chỉ thất bại trong một build Debug, và sẽ được gỡ bỏ tại thời gian biên dịch trong một xây dựng phát hành.
yoyo

2

Có một luồng lớn liên quan đến việc bật / tắt các xác nhận trong bản phát hành được xây dựng trên comp.lang.c ++. Được kiểm duyệt, nếu bạn có một vài tuần, bạn có thể thấy các ý kiến ​​khác nhau về vấn đề này như thế nào. :)

Trái ngược với coppro , tôi tin rằng nếu bạn không chắc chắn rằng một xác nhận có thể bị vô hiệu hóa trong bản dựng phát hành, thì nó không nên là một khẳng định. Các xác nhận là để bảo vệ chống lại các bất biến chương trình bị phá vỡ. Trong trường hợp như vậy, liên quan đến ứng dụng khách mã của bạn, sẽ có một trong hai kết quả có thể xảy ra:

  1. Chết với một số loại lỗi hệ điều hành, dẫn đến một cuộc gọi để hủy bỏ. (Không khẳng định)
  2. Chết qua một cuộc gọi trực tiếp để hủy bỏ. (Có xác nhận)

Không có sự khác biệt đối với người dùng, tuy nhiên, có thể các xác nhận đã thêm một chi phí hiệu năng không cần thiết vào mã có trong phần lớn các lần chạy mà mã không bị lỗi.

Câu trả lời cho câu hỏi thực sự phụ thuộc nhiều vào khách hàng của API sẽ là ai. Nếu bạn đang viết thư viện cung cấp API, thì bạn cần một số dạng cơ chế để thông báo cho khách hàng của mình rằng họ đã sử dụng API không chính xác. Trừ khi bạn cung cấp hai phiên bản của thư viện (một phiên bản có xác nhận, một phiên bản không có) thì khẳng định rất khó có thể là sự lựa chọn phù hợp.

Cá nhân, tuy nhiên, tôi không chắc chắn rằng tôi sẽ đi với ngoại lệ cho trường hợp này. Các ngoại lệ phù hợp hơn với nơi có thể diễn ra một hình thức phục hồi phù hợp. Ví dụ, có thể bạn đang cố gắng phân bổ bộ nhớ. Khi bạn bắt gặp ngoại lệ 'std :: bad_alloc', có thể giải phóng bộ nhớ và thử lại.


2

Tôi đã phác thảo quan điểm của tôi về trạng thái của vấn đề ở đây: Làm thế nào để bạn xác nhận trạng thái bên trong của một đối tượng? . Nói chung, khẳng định yêu cầu của bạn và ném cho người khác vi phạm. Để vô hiệu hóa các xác nhận trong bản dựng phát hành, bạn có thể làm:

  • Vô hiệu hóa xác nhận cho các kiểm tra đắt tiền (như kiểm tra xem một phạm vi có được đặt hàng không)
  • Giữ bật kiểm tra tầm thường (như kiểm tra con trỏ null hoặc giá trị boolean)

Tất nhiên, trong các bản dựng phát hành, các xác nhận thất bại và các ngoại lệ chưa được xử lý nên được xử lý theo cách khác so với các bản dựng gỡ lỗi (trong đó nó chỉ có thể gọi std :: abort). Viết nhật ký lỗi ở đâu đó (có thể vào một tệp), thông báo cho khách hàng rằng đã xảy ra lỗi nội bộ. Khách hàng sẽ có thể gửi cho bạn tệp nhật ký.


1

bạn đang hỏi về sự khác biệt giữa lỗi thời gian thiết kế và thời gian chạy.

các xác nhận là 'hey lập trình viên, đây là thông báo bị hỏng', chúng ở đó để nhắc nhở bạn về các lỗi bạn sẽ không nhận thấy khi chúng xảy ra.

trường hợp ngoại lệ là thông báo 'người dùng, đôi khi đã sai' (rõ ràng bạn có thể mã để bắt chúng để người dùng không bao giờ được thông báo) nhưng chúng được thiết kế để xảy ra vào thời gian chạy khi người dùng Joe đang sử dụng ứng dụng.

Vì vậy, nếu bạn nghĩ rằng bạn có thể loại bỏ tất cả các lỗi của mình, chỉ sử dụng ngoại lệ. Nếu bạn nghĩ rằng bạn không thể ..... sử dụng ngoại lệ. Tất nhiên, bạn vẫn có thể sử dụng các xác nhận gỡ lỗi để làm cho số lượng ngoại lệ ít hơn.

Đừng quên rằng nhiều điều kiện tiên quyết sẽ là dữ liệu do người dùng cung cấp, vì vậy bạn sẽ cần một cách tốt để thông báo cho người dùng rằng dữ liệu của anh ta không tốt. Để làm điều đó, bạn thường sẽ cần phải trả lại dữ liệu lỗi xuống ngăn xếp cuộc gọi cho các bit mà anh ta đang tương tác. Các xác nhận sẽ không hữu ích sau đó - gấp đôi vì vậy nếu ứng dụng của bạn là n-tier.

Cuối cùng, tôi không sử dụng - mã lỗi vượt trội hơn nhiều so với các lỗi bạn nghĩ sẽ xảy ra thường xuyên. :)


0

Tôi thích cái thứ hai. Trong khi các bài kiểm tra của bạn có thể chạy tốt, Murphy nói rằng điều gì đó bất ngờ sẽ xảy ra. Vì vậy, thay vì nhận được một ngoại lệ trong lệnh gọi phương thức sai thực tế, cuối cùng bạn sẽ tìm ra một khung xếp chồng 10 NullPulumException (hoặc tương đương) sâu hơn.


0

Các câu trả lời trước là chính xác: sử dụng ngoại lệ cho các hàm API công khai. Lần duy nhất bạn có thể muốn bẻ cong quy tắc này là khi chi phiếu được tính toán đắt đỏ. Trong trường hợp đó, bạn có thể đặt nó trong một khẳng định.

Nếu bạn nghĩ rằng vi phạm điều kiện tiên quyết đó có khả năng, hãy giữ nó như một ngoại lệ hoặc tái cấu trúc lại điều kiện tiên quyết đó.


0

Bạn nên sử dụng cả hai. Các xác nhận là để thuận tiện cho bạn như là một nhà phát triển. Ngoại lệ bắt những thứ bạn bỏ lỡ hoặc không mong đợi trong thời gian chạy.

Tôi đã yêu thích các chức năng báo cáo lỗi của glib thay vì các xác nhận cũ đơn giản. Họ hành xử như những tuyên bố khẳng định nhưng thay vì tạm dừng chương trình, họ chỉ trả lại một giá trị và để chương trình tiếp tục. Nó hoạt động tốt một cách đáng ngạc nhiên, và như một phần thưởng bạn có thể thấy những gì xảy ra với phần còn lại của chương trình của bạn khi một chức năng không trả về "những gì nó được cho là". Nếu nó gặp sự cố, bạn biết rằng việc kiểm tra lỗi của bạn bị lỏng lẻo ở một nơi khác.

Trong dự án cuối cùng của tôi, tôi đã sử dụng các kiểu hàm này để thực hiện kiểm tra điều kiện tiên quyết và nếu một trong số chúng thất bại, tôi sẽ in dấu vết ngăn xếp vào tệp nhật ký nhưng tiếp tục chạy. Tiết kiệm cho tôi hàng tấn thời gian gỡ lỗi khi người khác gặp sự cố khi chạy bản dựng gỡ lỗi của tôi.

#ifdef DEBUG
#define RETURN_IF_FAIL(expr)      do {                      \
 if (!(expr))                                           \
 {                                                      \
     fprintf(stderr,                                        \
        "file %s: line %d (%s): precondition `%s' failed.", \
        __FILE__,                                           \
        __LINE__,                                           \
        __PRETTY_FUNCTION__,                                \
        #expr);                                             \
     ::print_stack_trace(2);                                \
     return;                                                \
 };               } while(0)
#define RETURN_VAL_IF_FAIL(expr, val)  do {                         \
 if (!(expr))                                                   \
 {                                                              \
    fprintf(stderr,                                             \
        "file %s: line %d (%s): precondition `%s' failed.",     \
        __FILE__,                                               \
        __LINE__,                                               \
        __PRETTY_FUNCTION__,                                    \
        #expr);                                                 \
     ::print_stack_trace(2);                                    \
     return val;                                                \
 };               } while(0)
#else
#define RETURN_IF_FAIL(expr)
#define RETURN_VAL_IF_FAIL(expr, val)
#endif

Nếu tôi cần kiểm tra thời gian chạy đối số, tôi sẽ làm điều này:

char *doSomething(char *ptr)
{
    RETURN_VAL_IF_FAIL(ptr != NULL, NULL);  // same as assert(ptr != NULL), but returns NULL if it fails.
                                            // Goes away when debug off.

    if( ptr != NULL )
    {
       ...
    }

    return ptr;
}

Tôi không nghĩ rằng tôi đã thấy trong OP câu hỏi bất cứ điều gì liên quan đến C ++. Tôi tin rằng nó không nên được bao gồm trong câu trả lời của bạn.
ForceMagic

@ForceMagic: Câu hỏi đã có thẻ C ++ vào năm 2008 khi tôi đăng câu trả lời này và trên thực tế, thẻ C ++ đã bị xóa chỉ 5 giờ trước. Bất kể, mã minh họa một khái niệm độc lập với ngôn ngữ.
indiv

0

Tôi đã thử tổng hợp một số câu trả lời khác ở đây với quan điểm của riêng tôi.

Sử dụng các xác nhận cho các trường hợp bạn muốn vô hiệu hóa nó trong sản xuất, lỗi khi để chúng vào. Lý do thực sự duy nhất để vô hiệu hóa trong sản xuất, nhưng không phát triển, là để tăng tốc chương trình. Trong hầu hết các trường hợp, việc tăng tốc độ này sẽ không đáng kể, nhưng đôi khi mã là thời gian quan trọng hoặc thử nghiệm tốn kém về mặt tính toán. Nếu mã là nhiệm vụ quan trọng, thì ngoại lệ có thể là tốt nhất mặc dù chậm.

Nếu có bất kỳ cơ hội phục hồi thực sự nào, hãy sử dụng một ngoại lệ vì các xác nhận không được thiết kế để được phục hồi. Ví dụ, mã hiếm khi được thiết kế để khôi phục từ các lỗi lập trình, nhưng nó được thiết kế để khôi phục từ các yếu tố như lỗi mạng hoặc các tệp bị khóa. Lỗi không nên được xử lý như ngoại lệ đơn giản là nằm ngoài sự kiểm soát của lập trình viên. Thay vào đó, khả năng dự đoán của các lỗi này, so với các lỗi mã hóa, khiến chúng dễ phục hồi hơn.

Lập luận lại rằng việc gỡ lỗi các xác nhận sẽ dễ dàng hơn: Dấu vết ngăn xếp từ một ngoại lệ được đặt tên đúng cũng dễ đọc như một xác nhận. Mã tốt chỉ nên bắt các loại ngoại lệ cụ thể, vì vậy các ngoại lệ không được chú ý do bị bắt. Tuy nhiên, tôi nghĩ Java đôi khi buộc bạn phải nắm bắt mọi ngoại lệ.


0

Theo tôi, nguyên tắc cơ bản là sử dụng các biểu thức khẳng định để tìm các lỗi bên trong và các ngoại lệ cho các lỗi bên ngoài. Bạn có thể hưởng lợi nhiều từ cuộc thảo luận sau đây của Greg từ đây .

Các biểu thức khẳng định được sử dụng để tìm lỗi lập trình: hoặc là lỗi trong chính logic của chương trình hoặc lỗi trong quá trình thực hiện tương ứng. Một điều kiện khẳng định xác minh rằng chương trình vẫn ở trạng thái xác định. Một "trạng thái xác định" về cơ bản là một trạng thái đồng ý với các giả định của chương trình. Lưu ý rằng "trạng thái được xác định" cho chương trình không cần phải là "trạng thái lý tưởng" hay thậm chí là "trạng thái thông thường", hoặc thậm chí là "trạng thái hữu ích" mà nhiều hơn về điểm quan trọng đó sau này.

Để hiểu cách các xác nhận phù hợp với một chương trình, hãy xem xét một thói quen trong chương trình C ++ sắp sửa hủy bỏ một con trỏ. Bây giờ nên kiểm tra thường xuyên xem con trỏ có phải là NULL trước khi hủy hội nghị hay không, hay nó sẽ khẳng định rằng con trỏ không phải là NULL và sau đó tiếp tục và loại bỏ nó bất kể?

Tôi tưởng tượng rằng hầu hết các nhà phát triển sẽ muốn làm cả hai, thêm xác nhận, nhưng cũng kiểm tra con trỏ để tìm giá trị NULL, để không gặp sự cố nếu điều kiện được xác nhận thất bại. Nhìn bề ngoài, thực hiện cả kiểm tra và kiểm tra có vẻ là quyết định khôn ngoan nhất

Không giống như các điều kiện đã được khẳng định của nó, việc xử lý lỗi của chương trình (ngoại lệ) không chỉ là lỗi trong chương trình, mà là các đầu vào mà chương trình thu được từ môi trường của nó. Đây thường là "lỗi" trên một phần của ai đó, chẳng hạn như người dùng đang cố đăng nhập vào tài khoản mà không nhập mật khẩu. Và mặc dù lỗi có thể ngăn việc hoàn thành thành công nhiệm vụ của chương trình, không có lỗi chương trình. Chương trình không đăng nhập được người dùng mà không có mật khẩu do lỗi bên ngoài - lỗi về phía người dùng. Nếu hoàn cảnh khác nhau và người dùng nhập đúng mật khẩu và chương trình không thể nhận ra nó; sau đó mặc dù kết quả vẫn như vậy, nhưng thất bại bây giờ thuộc về chương trình.

Mục đích của xử lý lỗi (ngoại lệ) là hai lần. Đầu tiên là liên lạc với người dùng (hoặc một số khách hàng khác) rằng lỗi trong đầu vào của chương trình đã được phát hiện và ý nghĩa của nó. Mục đích thứ hai là khôi phục ứng dụng sau khi phát hiện lỗi, về trạng thái được xác định rõ. Lưu ý rằng bản thân chương trình không có lỗi trong tình huống này. Cấp, chương trình có thể ở trạng thái không lý tưởng, hoặc thậm chí là trạng thái không thể làm gì hữu ích, nhưng không có lỗi lập trình. Ngược lại, do trạng thái phục hồi lỗi là do một thiết kế của chương trình dự đoán, nên nó là một trạng thái mà chương trình có thể xử lý.

PS: bạn có thể muốn kiểm tra câu hỏi tương tự: Exception Vs Ass Ass .


-1

Xem thêm câu hỏi này :

Tôi một số trường hợp, khẳng định bị vô hiệu hóa khi xây dựng để phát hành. Bạn có thể không có quyền kiểm soát này (nếu không, bạn có thể xây dựng với các xác nhận trên), vì vậy có thể là một ý tưởng tốt để làm điều đó như thế này.

Vấn đề với việc "sửa" các giá trị đầu vào là người gọi sẽ không nhận được những gì họ mong đợi và điều này có thể dẫn đến sự cố hoặc thậm chí gặp sự cố trong các phần hoàn toàn khác nhau của chương trình, khiến việc gỡ lỗi trở thành cơn ác mộng.

Tôi thường đưa ra một ngoại lệ trong câu lệnh if để đảm nhận vai trò của khẳng định trong trường hợp chúng bị vô hiệu hóa

assert(value>0);
if(value<=0) throw new ArgumentOutOfRangeException("value");
//do stuff
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.