nhưng sự cố phần mềm của khách hàng của bạn vẫn không phải là một điều tốt
Nó chắc chắn là một điều tốt.
Bạn muốn bất cứ điều gì khiến hệ thống ở trạng thái không xác định sẽ dừng hệ thống vì một hệ thống không xác định có thể làm những việc khó chịu như dữ liệu bị hỏng, định dạng ổ cứng và gửi email đe dọa tổng thống. Nếu bạn không thể khôi phục và đưa hệ thống trở lại trạng thái xác định thì sự cố là việc phải làm. Đó chính xác là lý do tại sao chúng tôi xây dựng các hệ thống gặp sự cố thay vì lặng lẽ tự xé toạc. Bây giờ chắc chắn, tất cả chúng ta đều muốn một hệ thống ổn định không bao giờ gặp sự cố nhưng chúng tôi chỉ thực sự muốn điều đó khi hệ thống ở trong trạng thái an toàn có thể dự đoán được xác định.
Tôi đã nghe nói rằng các trường hợp ngoại lệ như luồng điều khiển được coi là một phản hạt nghiêm trọng
Điều đó hoàn toàn đúng nhưng nó thường bị hiểu lầm. Khi họ phát minh ra hệ thống ngoại lệ, họ sợ rằng họ đã phá vỡ lập trình có cấu trúc. Lập trình có cấu trúc là lý do tại sao chúng tôi có for
, while
, until
, break
, và continue
khi tất cả chúng ta cần, để làm tất cả điều đó, là goto
.
Dijkstra đã dạy chúng tôi rằng sử dụng goto một cách không chính thức (nghĩa là nhảy xung quanh bất cứ nơi nào bạn muốn) khiến việc đọc mã trở thành một cơn ác mộng. Khi họ đưa cho chúng tôi hệ thống ngoại lệ, họ sợ rằng họ đang phát minh lại goto. Vì vậy, họ bảo chúng tôi không "sử dụng nó để kiểm soát dòng chảy" với hy vọng chúng tôi hiểu. Thật không may, nhiều người trong chúng ta đã không.
Kỳ lạ thay, chúng ta thường không lạm dụng các ngoại lệ để tạo mã spaghetti như trước đây với goto. Những lời khuyên dường như đã gây ra nhiều rắc rối.
Các ngoại lệ cơ bản là về việc từ chối một giả định. Khi bạn yêu cầu một tệp được lưu, bạn cho rằng tệp đó có thể và sẽ được lưu. Ngoại lệ bạn nhận được khi không thể là tên bất hợp pháp, HD đầy hoặc do một con chuột đã gặm nhấm cáp dữ liệu của bạn. Bạn có thể xử lý tất cả các lỗi đó một cách khác nhau, bạn có thể xử lý tất cả các lỗi theo cùng một cách hoặc bạn có thể để chúng dừng hệ thống. Có một đường dẫn hạnh phúc trong mã của bạn, nơi các giả định của bạn phải giữ đúng. Một cách hay một ngoại lệ khác đưa bạn ra khỏi con đường hạnh phúc đó. Nói đúng ra, ừ đó là một loại "kiểm soát dòng chảy" nhưng đó không phải là điều họ đang cảnh báo bạn. Họ đã nói về những điều vô nghĩa như thế này :
"Trường hợp ngoại lệ nên được đặc biệt". Tautology nhỏ này đã được sinh ra bởi vì các nhà thiết kế hệ thống ngoại lệ cần thời gian để xây dựng dấu vết ngăn xếp. So với nhảy xung quanh, điều này là chậm. Nó ăn thời gian CPU. Nhưng nếu bạn chuẩn bị đăng nhập và tạm dừng hệ thống hoặc ít nhất là tạm dừng quá trình xử lý chuyên sâu thời gian hiện tại trước khi bắt đầu kế tiếp thì bạn có một chút thời gian để giết. Nếu mọi người bắt đầu sử dụng ngoại lệ "để kiểm soát dòng chảy" thì những giả định đó về thời gian sẽ xuất hiện ngoài cửa sổ. Vì vậy, "Ngoại lệ nên là ngoại lệ" thực sự được đưa ra cho chúng tôi như là một xem xét hiệu suất.
Quan trọng hơn nhiều so với điều đó không làm chúng ta bối rối. Mất bao lâu để bạn phát hiện ra vòng lặp vô hạn trong đoạn mã trên?
KHÔNG trả lại mã lỗi.
... là lời khuyên tốt khi bạn ở trong một cơ sở mã thường không sử dụng mã lỗi. Tại sao? Bởi vì không ai sẽ nhớ lưu giá trị trả về và kiểm tra mã lỗi của bạn. Đó vẫn là một quy ước tốt khi bạn ở C.
OneOf
Bạn đang sử dụng một quy ước khác. Điều đó tốt miễn là bạn thiết lập quy ước và không chỉ đơn giản là chiến đấu với người khác. Thật khó hiểu khi có hai quy ước lỗi trong cùng một cơ sở mã. Nếu bằng cách nào đó bạn đã thoát khỏi tất cả các mã sử dụng quy ước khác thì hãy tiếp tục.
Tôi thích bản quy ước. Một trong những lời giải thích tốt nhất về nó tôi tìm thấy ở đây * :
Nhưng nhiều như tôi thích, tôi vẫn sẽ không trộn lẫn nó với các quy ước khác. Chọn một và gắn bó với nó. 1
1: Ý tôi là đừng khiến tôi phải suy nghĩ về nhiều hơn một quy ước cùng một lúc.
Suy nghĩ tiếp theo:
Từ cuộc thảo luận này, những gì tôi đang lấy đi hiện tại như sau:
- Nếu bạn mong đợi người gọi ngay lập tức bắt và xử lý ngoại lệ hầu hết thời gian và tiếp tục công việc của mình, có lẽ thông qua một đường dẫn khác, nó có thể là một phần của kiểu trả về. Tùy chọn hoặc OneOf có thể hữu ích ở đây.
- Nếu bạn mong muốn người gọi ngay lập tức không bắt được ngoại lệ hầu hết thời gian, hãy ném một ngoại lệ, để tiết kiệm sự tỉnh táo của việc chuyển nó thủ công lên ngăn xếp.
- Nếu bạn không chắc chắn người gọi ngay lập tức sẽ làm gì, có thể cung cấp cả hai, như Parse và TryPude.
Nó thực sự không đơn giản. Một trong những điều cơ bản bạn cần hiểu là số 0 là gì.
Tháng 5 còn lại bao nhiêu ngày? 0 (vì không phải là tháng 5. Đã là tháng 6 rồi).
Ngoại lệ là một cách để từ chối một giả định nhưng chúng không phải là cách duy nhất. Nếu bạn sử dụng ngoại lệ để từ chối giả định, bạn sẽ rời khỏi con đường hạnh phúc. Nhưng nếu bạn chọn các giá trị để gửi xuống đường dẫn hạnh phúc báo hiệu rằng mọi thứ không đơn giản như đã giả định thì bạn có thể ở trên đường đó miễn là nó có thể xử lý các giá trị đó. Đôi khi 0 đã được sử dụng để có nghĩa là một cái gì đó vì vậy bạn phải tìm một giá trị khác để ánh xạ ý tưởng từ chối giả định của bạn. Bạn có thể nhận ra ý tưởng này từ việc sử dụng nó trong đại số cũ tốt . Các đơn nguyên có thể giúp với điều đó nhưng không phải lúc nào cũng phải là một đơn nguyên.
Ví dụ 2 :
IList<int> ParseAllTheInts(String s) { ... }
Bạn có thể nghĩ ra bất kỳ lý do chính đáng nào mà nó phải được thiết kế để nó cố tình ném bất cứ thứ gì bao giờ không? Đoán xem bạn nhận được gì khi không có int có thể được phân tích cú pháp? Tôi thậm chí không cần nói với bạn.
Đó là một dấu hiệu của một cái tên hay. Xin lỗi nhưng TryPude không phải là ý tưởng của tôi về một cái tên hay.
Chúng ta thường tránh ném ngoại lệ vào việc không nhận được gì khi câu trả lời có thể nhiều hơn một điều cùng một lúc nhưng vì một số lý do nếu câu trả lời là một điều hoặc không có gì chúng ta bị ám ảnh khi khăng khăng rằng nó cho chúng ta một điều hoặc ném:
IList<Point> Intersection(Line a, Line b) { ... }
Các đường song song có thực sự cần phải gây ra một ngoại lệ ở đây không? Có thực sự xấu nếu danh sách này sẽ không bao giờ chứa nhiều hơn một điểm?
Có thể về mặt ngữ nghĩa, bạn không thể thực hiện điều đó. Nếu vậy, thật đáng tiếc. Nhưng có lẽ Monads, không có kích thước tùy ý như List
vậy, sẽ khiến bạn cảm thấy tốt hơn về nó.
Maybe<Point> Intersection(Line a, Line b) { ... }
Các Monads là những bộ sưu tập mục đích đặc biệt nhỏ được sử dụng theo những cách cụ thể mà không cần phải kiểm tra chúng. Chúng ta phải tìm cách đối phó với chúng bất kể chúng chứa gì. Bằng cách đó, con đường hạnh phúc vẫn đơn giản. Nếu bạn mở và kiểm tra mọi Monad bạn chạm vào thì bạn đang sử dụng sai.
Tôi biết, nó thật kỳ lạ. Nhưng đó là một công cụ mới (tốt, với chúng tôi). Vì vậy, cho nó một chút thời gian. Búa có ý nghĩa hơn khi bạn ngừng sử dụng chúng trên ốc vít.
Nếu bạn thưởng thức tôi, tôi muốn giải quyết nhận xét này:
Tại sao không có câu trả lời nào làm rõ rằng đơn vị Either không phải là mã lỗi và OneOf cũng không? Chúng khác nhau về cơ bản, và câu hỏi do đó dường như được dựa trên một sự hiểu lầm. (Mặc dù ở dạng đã được sửa đổi, đây vẫn là một câu hỏi hợp lệ.) - Konrad Rudolph ngày 4 tháng 6 `18 lúc 14:08
Điều này là hoàn toàn đúng. Monads gần với các bộ sưu tập hơn các trường hợp ngoại lệ, cờ hoặc mã lỗi. Họ làm những thùng chứa tốt cho những thứ như vậy khi được sử dụng một cách khôn ngoan.
Result
;-)