Tại sao thiết kế một ngôn ngữ hiện đại mà không có cơ chế xử lý ngoại lệ?


47

Nhiều ngôn ngữ hiện đại cung cấp các tính năng xử lý ngoại lệ phong phú , nhưng ngôn ngữ lập trình Swift của Apple không cung cấp cơ chế xử lý ngoại lệ .

Bị cuốn theo những ngoại lệ như tôi, tôi gặp khó khăn trong việc suy nghĩ về ý nghĩa của nó. Swift có các xác nhận, và tất nhiên trả về các giá trị; nhưng tôi gặp khó khăn khi hình dung cách suy nghĩ dựa trên ngoại lệ của tôi ánh xạ đến một thế giới mà không có ngoại lệ (và, đối với vấn đề đó, tại sao một thế giới như vậy lại được mong muốn ). Có những điều tôi không thể làm bằng ngôn ngữ như Swift mà tôi có thể làm với ngoại lệ không? Tôi có đạt được điều gì bằng cách mất ngoại lệ không?

Làm thế nào ví dụ tôi có thể thể hiện tốt nhất một cái gì đó như

try:
    operation_that_can_throw_ioerror()
except IOError:
    handle_the_exception_somehow()
else:
     # we don't want to catch the IOError if it's raised
    another_operation_that_can_throw_ioerror()
finally:
    something_we_always_need_to_do()

trong một ngôn ngữ (ví dụ Swift) mà thiếu xử lý ngoại lệ?


11
Bạn có thể muốn thêm vào danh sách , nếu chúng ta bỏ qua paniccái không hoàn toàn giống nhau. Ngoài những gì được nói ở đó, một ngoại lệ không khác gì một cách tinh vi (nhưng thoải mái) để thực hiện a GOTO, mặc dù không ai gọi nó theo cách đó, vì những lý do rõ ràng.
JensG

3
Câu trả lời sắp xếp cho câu hỏi của bạn là bạn cần hỗ trợ ngôn ngữ cho các trường hợp ngoại lệ để viết chúng. Hỗ trợ ngôn ngữ thường bao gồm quản lý bộ nhớ; vì một ngoại lệ có thể được ném ở bất cứ đâu và bị bắt ở bất cứ đâu, cần có một cách để loại bỏ các vật thể không dựa vào luồng điều khiển.
Robert Harvey

1
Tôi không hoàn toàn theo dõi bạn, @Robert. C ++ quản lý để hỗ trợ các ngoại lệ mà không cần thu gom rác.
Karl Bielefeldt

3
@KarlBielefeldt: Với chi phí lớn, từ những gì tôi hiểu. Sau đó, một lần nữa, có bất cứ điều gì được thực hiện trong C ++ mà không phải trả chi phí lớn, ít nhất là trong nỗ lực và kiến ​​thức tên miền cần thiết?
Robert Harvey

2
@RobertHarvey: Điểm tốt. Tôi là một trong những người không suy nghĩ đủ về những điều này. Tôi đã bị ru ngủ khi nghĩ rằng ARC là GC, nhưng, tất nhiên, không phải vậy. Vì vậy, về cơ bản (nếu tôi đang nắm bắt, đại khái), các trường hợp ngoại lệ sẽ là một điều lộn xộn (mặc dù C ++) trong một ngôn ngữ mà việc xử lý các đối tượng dựa vào luồng điều khiển?
orome

Câu trả lời:


33

Trong lập trình nhúng, các trường hợp ngoại lệ theo truyền thống không được phép, bởi vì chi phí chung của ngăn xếp mà bạn phải làm được coi là một biến đổi không thể chấp nhận khi cố gắng duy trì hiệu suất thời gian thực. Mặc dù về mặt kỹ thuật, điện thoại thông minh có thể được coi là nền tảng thời gian thực, nhưng giờ đây chúng đủ mạnh, nơi những hạn chế cũ của các hệ thống nhúng không thực sự được áp dụng nữa. Tôi chỉ mang nó lên vì sự kỹ lưỡng.

Các ngoại lệ thường được hỗ trợ trong các ngôn ngữ lập trình chức năng, nhưng hiếm khi được sử dụng đến mức chúng có thể không được. Một lý do là đánh giá lười biếng, đôi khi được thực hiện ngay cả trong các ngôn ngữ không lười biếng theo mặc định. Có một hàm thực thi với một ngăn xếp khác với vị trí được xếp hàng để thực thi khiến cho việc xác định nơi đặt trình xử lý ngoại lệ của bạn trở nên khó khăn.

Lý do khác là các hàm hạng nhất cho phép các cấu trúc như tùy chọn và tương lai cung cấp cho bạn lợi ích cú pháp của các ngoại lệ với tính linh hoạt cao hơn. Nói cách khác, phần còn lại của ngôn ngữ đủ biểu cảm để các ngoại lệ không mua cho bạn bất cứ thứ gì.

Tôi không quen với Swift, nhưng tôi ít đọc về cách xử lý lỗi của nó cho thấy họ có ý định xử lý lỗi để theo các kiểu kiểu chức năng hơn. Tôi đã thấy các ví dụ mã với successfailurecác khối trông rất giống tương lai.

Đây là một ví dụ sử dụng Futuretừ hướng dẫn Scala này :

val f: Future[List[String]] = future {
  session.getRecentPosts
}
f onFailure {
  case t => println("An error has occured: " + t.getMessage)
}
f onSuccess {
  case posts => for (post <- posts) println(post)
}

Bạn có thể thấy nó có cấu trúc gần giống như ví dụ của bạn bằng cách sử dụng các ngoại lệ. Các futurekhối giống như một try. Các onFailurekhối giống như một xử lý ngoại lệ. Trong Scala, như trong hầu hết các ngôn ngữ chức năng, Futuređược thực hiện hoàn toàn bằng chính ngôn ngữ đó. Nó không yêu cầu bất kỳ cú pháp đặc biệt như ngoại lệ nào. Điều đó có nghĩa là bạn có thể xác định các cấu trúc tương tự của riêng bạn. Có thể thêm một timeoutkhối, ví dụ, hoặc chức năng đăng nhập.

Ngoài ra, bạn có thể vượt qua tương lai, trả lại từ hàm, lưu trữ trong cấu trúc dữ liệu hoặc bất cứ điều gì. Đó là một giá trị hạng nhất. Bạn không bị giới hạn như các trường hợp ngoại lệ phải được truyền thẳng lên ngăn xếp.

Các tùy chọn giải quyết vấn đề xử lý lỗi theo một cách hơi khác, hoạt động tốt hơn đối với một số trường hợp sử dụng. Bạn không bị mắc kẹt chỉ với một phương pháp.

Đó là những thứ bạn "có được bằng cách mất ngoại lệ."


1
Vì vậy, Futurevề cơ bản là một cách kiểm tra giá trị được trả về từ một hàm, mà không dừng lại để chờ đợi nó. Giống như Swift, nó dựa trên giá trị trả về, nhưng không giống như Swift, phản hồi về giá trị trả về có thể xảy ra sau đó (hơi giống ngoại lệ). Đúng?
orome

1
Bạn hiểu một Futurecách chính xác, nhưng tôi nghĩ rằng bạn có thể đang nhầm lẫn Swift. Xem phần đầu tiên của câu trả lời stackoverflow này , ví dụ.
Karl Bielefeldt

Hmm, tôi mới biết về Swift, vì vậy câu trả lời đó hơi khó đối với tôi. Nhưng nếu tôi không nhầm: điều đó về cơ bản vượt qua một trình xử lý có thể được gọi sau đó; đúng?
orome

Đúng. Về cơ bản bạn đang tạo một cuộc gọi lại khi xảy ra lỗi.
Karl Bielefeldt

Eithersẽ là một ví dụ tốt hơn IMHO
Paweł Prażak

19

Các ngoại lệ có thể làm cho mã khó khăn hơn để lý luận. Mặc dù chúng không mạnh như gotos, nhưng chúng có thể gây ra nhiều vấn đề tương tự do bản chất không phải là địa phương của chúng. Ví dụ: giả sử bạn có một đoạn mã bắt buộc như thế này:

cleanMug();
brewCoffee();
pourCoffee();
drinkCoffee();

Bạn không thể biết nhanh liệu bất kỳ thủ tục nào trong số này có thể đưa ra một ngoại lệ. Bạn phải đọc tài liệu của từng thủ tục để tìm ra điều đó. (Một số ngôn ngữ làm cho việc này dễ dàng hơn một chút bằng cách tăng chữ ký loại với thông tin này.) Đoạn mã trên sẽ biên dịch tốt bất kể mọi thủ tục có bị ném hay không, khiến cho việc xử lý ngoại lệ thực sự dễ dàng.

Ngoài ra, ngay cả khi mục đích là truyền ngoại lệ trở lại cho người gọi, người ta thường cần thêm mã bổ sung để ngăn không cho mọi thứ ở trạng thái không nhất quán (ví dụ: nếu máy pha cà phê của bạn bị hỏng, bạn vẫn cần dọn dẹp mớ hỗn độn và trả lại cái cốc!). Do đó, trong nhiều trường hợp, mã sử dụng các ngoại lệ sẽ trông phức tạp như mã không có vì phải dọn thêm.

Các ngoại lệ có thể được mô phỏng với một hệ thống loại đủ mạnh. Nhiều ngôn ngữ tránh ngoại lệ sử dụng các giá trị trả về để có cùng hành vi. Nó tương tự như cách nó được thực hiện trong C, nhưng các hệ thống loại hiện đại thường làm cho nó thanh lịch hơn và cũng khó quên hơn để xử lý tình trạng lỗi. Họ cũng có thể cung cấp đường cú pháp để làm cho mọi thứ bớt cồng kềnh, đôi khi gần như sạch sẽ như ngoại lệ.

Cụ thể, bằng cách nhúng xử lý lỗi vào hệ thống loại thay vì triển khai như một tính năng riêng biệt cho phép "ngoại lệ" được sử dụng cho những thứ khác thậm chí không liên quan đến lỗi. (Người ta biết rằng xử lý ngoại lệ thực sự là một tài sản của các đơn nguyên.)


Có đúng là loại hệ thống mà Swift có, bao gồm các tùy chọn, là loại "hệ thống loại mạnh" đạt được điều này không?
orome

2
Có, các tùy chọn và, nói chung hơn, các loại tổng (được gọi là "enum" trong Swift / Rust) có thể đạt được điều này. Tuy nhiên, phải mất một số công việc bổ sung để làm cho chúng dễ sử dụng, tuy nhiên: trong Swift, điều này đạt được với cú pháp chuỗi tùy chọn; trong Haskell, điều này đạt được với ký hiệu đơn âm.
Rufflewind

"Hệ thống loại đủ mạnh" có thể cho dấu vết ngăn xếp không, nếu không nó khá vô dụng
Paweł Prażak

1
+1 để chỉ ra rằng các ngoại lệ che khuất luồng kiểm soát. Cũng giống như một ghi chú phụ trợ: Lý do là liệu các ngoại lệ không thực sự xấu hơn goto: gotoBị giới hạn ở một chức năng duy nhất, đó là một phạm vi khá nhỏ được cung cấp thực sự rất nhỏ, ngoại lệ hoạt động giống như một số chéo gotocome from(xem vi.wikipedia.org/wiki/INTERCAL ;-)). Nó có thể kết nối khá nhiều bất kỳ hai đoạn mã nào, có thể bỏ qua mã một số chức năng thứ ba. Điều duy nhất nó không thể làm, gotocó thể, là quay trở lại.
cmaster

2
@ PawełPrażak Khi làm việc với nhiều hàm bậc cao hơn, dấu vết ngăn xếp gần như không có giá trị. Đảm bảo mạnh mẽ về đầu vào và đầu ra và tránh các tác dụng phụ là những gì ngăn cản sự gián tiếp này gây ra lỗi khó hiểu.
Jack

11

Có một số câu trả lời tuyệt vời ở đây, nhưng tôi nghĩ một lý do quan trọng chưa được nhấn mạnh đủ: Khi ngoại lệ xảy ra, các đối tượng có thể bị bỏ lại ở trạng thái không hợp lệ. Nếu bạn có thể "bắt" một ngoại lệ, thì mã xử lý ngoại lệ của bạn sẽ có thể truy cập và làm việc với các đối tượng không hợp lệ đó. Điều đó sẽ trở nên sai lầm khủng khiếp trừ khi mã cho các đối tượng đó được viết hoàn hảo, điều này rất, rất khó thực hiện.

Ví dụ, tưởng tượng thực hiện Vector. Nếu ai đó khởi tạo Vector của bạn bằng một tập hợp các đối tượng, nhưng có một ngoại lệ xảy ra trong quá trình khởi tạo (có lẽ là trong khi sao chép các đối tượng của bạn vào bộ nhớ mới được phân bổ), rất khó để mã hóa chính xác việc thực hiện Vector theo cách không có bộ nhớ bị rò rỉ. Bài viết ngắn này của Stroustroup bao gồm ví dụ về Vector .

Và đó chỉ là phần nổi của tảng băng chìm. Điều gì xảy ra nếu, ví dụ, bạn đã sao chép một số yếu tố, nhưng không phải tất cả các yếu tố? Để triển khai một thùng chứa như Vector một cách chính xác, bạn gần như phải thực hiện mọi hành động bạn thực hiện có thể đảo ngược, do đó toàn bộ hoạt động là nguyên tử (như giao dịch cơ sở dữ liệu). Điều này rất phức tạp và hầu hết các ứng dụng đều hiểu sai. Và ngay cả khi nó được thực hiện một cách chính xác, nó làm phức tạp đáng kể quá trình thực hiện container.

Vì vậy, một số ngôn ngữ hiện đại đã quyết định nó không đáng. Ví dụ, Rust không có ngoại lệ, nhưng chúng không thể bị "bắt", vì vậy không có cách nào để mã tương tác với các đối tượng ở trạng thái không hợp lệ.


1
Mục đích của việc truy bắt là làm cho một đối tượng nhất quán (hoặc chấm dứt một cái gì đó nếu không thể) sau khi xảy ra lỗi.
JoulinRouge

@JoulinRouge Tôi biết. Nhưng một số ngôn ngữ đã quyết định không cung cấp cho bạn cơ hội đó, mà thay vào đó để phá vỡ toàn bộ quá trình. Những nhà thiết kế ngôn ngữ đó biết loại công việc dọn dẹp mà bạn muốn làm nhưng đã kết luận rằng thật quá khó để cung cấp cho bạn điều đó, và sự đánh đổi liên quan đến việc đó sẽ không đáng. Tôi nhận ra rằng bạn có thể không đồng ý với lựa chọn của họ ... nhưng thật có giá trị khi biết họ đã lựa chọn một cách có ý thức vì những lý do đặc biệt này.
Charlie Hoa

6

Một điều tôi ban đầu rất ngạc nhiên về ngôn ngữ Rust là nó không hỗ trợ các ngoại lệ. Bạn có thể đưa ra các ngoại lệ, nhưng chỉ có thời gian chạy mới có thể bắt được chúng khi một tác vụ (nghĩ luồng, nhưng không phải luôn luôn là một luồng HĐH riêng) chết; nếu bạn tự bắt đầu một nhiệm vụ, thì bạn có thể hỏi liệu nó đã thoát bình thường hay chưa fail!().

Vì vậy, nó không phải là thành ngữ failrất thường xuyên. Ví dụ, một vài trường hợp xảy ra là trong khai thác thử nghiệm (không biết mã người dùng là gì), là cấp cao nhất của trình biên dịch (hầu hết các trình biên dịch thay thế) hoặc khi gọi lại một cuộc gọi lại về đầu vào của người dùng.

Thay vào đó, các thành ngữ phổ biến là sử dụng các Resultmẫu để dứt khoát bỏ qua lỗi đó phải được xử lý. Này được thực hiện dễ dàng hơn đáng kể bởi các try!vĩ mô , đó là có thể được quấn quanh bất kỳ biểu hiện rằng sản lượng một quả và mang lại cánh tay thành công nếu có một, hoặc nếu không thì trả sớm từ chức năng.

use std::io::IoResult;
use std::io::File;

fn load_file(name: &Path) -> IoResult<String>
{
    let mut file = try!(File::open(name));
    let s = try!(file.read_to_string());
    return Ok(s);
}

fn main()
{
    print!("{}", load_file(&Path::new("/tmp/hello")).unwrap());
}

1
Vì vậy, có công bằng không khi nói rằng điều này cũng vậy (như cách tiếp cận của Go) tương tự như Swift, có assert, nhưng không catch?
orome

1
Trong Swift, hãy thử! có nghĩa là: Có tôi biết điều này có thể thất bại, nhưng tôi chắc chắn là nó sẽ không xảy ra, vì vậy tôi không xử lý nó và nếu nó thất bại thì mã của tôi bị sai, vì vậy xin vui lòng gặp sự cố trong trường hợp đó.
gnasher729

6

Theo tôi, ngoại lệ là một công cụ thiết yếu để phát hiện lỗi mã trong thời gian chạy. Cả trong thử nghiệm và trong sản xuất. Làm cho thông điệp của họ đủ dài để kết hợp với dấu vết ngăn xếp, bạn có thể tìm ra điều gì đã xảy ra từ nhật ký.

Các ngoại lệ chủ yếu là một công cụ phát triển và một cách để có được các báo cáo lỗi hợp lý từ sản xuất trong các trường hợp không mong muốn.

Ngoài việc phân tách các mối quan tâm (đường dẫn hạnh phúc chỉ có lỗi dự kiến ​​so với rơi vào cho đến khi gặp một số trình xử lý chung cho các lỗi không mong muốn) là một điều tốt, làm cho mã của bạn dễ đọc và dễ bảo trì hơn, thực tế không thể chuẩn bị mã của bạn cho tất cả có thể trường hợp bất ngờ, thậm chí bằng cách làm đầy nó với mã xử lý lỗi để hoàn thành không thể đọc được.

Đó thực sự là ý nghĩa của "bất ngờ".

Btw., Những gì được mong đợi và những gì không phải là một quyết định chỉ có thể được đưa ra tại trang web cuộc gọi. Đó là lý do tại sao các ngoại lệ được kiểm tra trong Java không giải quyết được - quyết định được đưa ra tại thời điểm phát triển API, khi nó không rõ ràng những gì được mong đợi hoặc bất ngờ.

Ví dụ đơn giản: API của bản đồ băm có thể có hai phương thức:

Value get(Key)

Option<Value> getOption(key)

lần đầu tiên ném một ngoại lệ nếu không tìm thấy, lần sau cung cấp cho bạn một giá trị tùy chọn. Trong một số trường hợp, cái sau có ý nghĩa hơn, nhưng trong những trường hợp khác, mã sin của bạn phải hy vọng có một giá trị cho một khóa đã cho, vì vậy nếu không có, đó là lỗi mà mã này không thể sửa được vì lỗi cơ bản giả định đã thất bại. Trong trường hợp này, đó thực sự là hành vi mong muốn thoát khỏi đường dẫn mã và xuống một số trình xử lý chung trong trường hợp cuộc gọi thất bại.

Mã không bao giờ nên cố gắng đối phó với các giả định cơ bản thất bại.

Ngoại trừ bằng cách kiểm tra chúng và ném các ngoại lệ dễ đọc, tất nhiên.

Ném ngoại lệ không phải là xấu nhưng bắt chúng có thể. Đừng cố gắng sửa các lỗi không mong muốn. Bắt ngoại lệ ở một vài nơi mà bạn muốn tiếp tục một số vòng lặp hoặc hoạt động, ghi nhật ký chúng, có thể báo cáo một lỗi không xác định và đó là lỗi.

Bắt các khối ở khắp nơi là một ý tưởng rất xấu.

Thiết kế API của bạn theo cách giúp bạn dễ dàng thể hiện ý định của mình, tức là tuyên bố xem bạn có mong đợi một trường hợp nào đó, như không tìm thấy khóa hay không. Người dùng API của bạn sau đó có thể chọn cuộc gọi ném cho những trường hợp thực sự bất ngờ.

Tôi đoán rằng lý do mà mọi người phẫn nộ ngoại lệ và đi quá xa bằng cách bỏ qua công cụ quan trọng này để tự động hóa xử lý lỗi và phân tách mối quan tâm tốt hơn khỏi các ngôn ngữ mới là những trải nghiệm tồi tệ.

Điều đó, và một số hiểu lầm về những gì họ thực sự tốt cho.

Mô phỏng chúng bằng cách thực hiện MỌI THỨ thông qua liên kết đơn âm làm cho mã của bạn không thể đọc và duy trì được, và bạn kết thúc mà không có dấu vết ngăn xếp, điều này làm cho cách tiếp cận này tồi tệ hơn.

Xử lý lỗi kiểu chức năng là tuyệt vời cho các trường hợp lỗi dự kiến.

Hãy để việc xử lý ngoại lệ tự động xử lý tất cả phần còn lại, đó là những gì dành cho :)


3

Swift sử dụng các nguyên tắc tương tự ở đây như Objective-C, do đó, nhiều hơn nữa. Trong Objective-C, các ngoại lệ chỉ ra lỗi lập trình. Chúng không được xử lý trừ các công cụ báo cáo sự cố. "Xử lý ngoại lệ" được thực hiện bằng cách sửa mã. . Swift chỉ cần loại bỏ khả năng để bắt ngoại lệ.

Swift có một tính năng trông giống như xử lý ngoại lệ nhưng chỉ là xử lý lỗi được thi hành. Về mặt lịch sử, Objective-C có mô hình xử lý lỗi khá phổ biến: Một phương thức sẽ trả về BOOL (CÓ cho thành công) hoặc tham chiếu đối tượng (không cho thất bại, không phải là thành công) và có tham số "con trỏ tới NSError *" mà sẽ được sử dụng để lưu trữ một tham chiếu NSError. Swift tự động chuyển đổi các cuộc gọi thành một phương thức như vậy thành một cái gì đó trông giống như xử lý ngoại lệ.

Nói chung, các hàm Swift có thể dễ dàng trả về các lựa chọn thay thế, như kết quả nếu một chức năng hoạt động tốt và lỗi nếu nó không thành công; Điều đó làm cho việc xử lý lỗi dễ dàng hơn rất nhiều. Nhưng câu trả lời cho câu hỏi ban đầu: Các nhà thiết kế Swift rõ ràng cảm thấy rằng việc tạo ra một ngôn ngữ an toàn và viết mã thành công bằng ngôn ngữ như vậy, sẽ dễ dàng hơn nếu ngôn ngữ không có ngoại lệ.


Đối với Swift, đây là câu trả lời đúng, IMO. Swift phải duy trì khả năng tương thích với các khung hệ thống Objective-C hiện có, do đó, trong trường hợp này, họ không có ngoại lệ truyền thống. Tôi đã viết một bài đăng trên blog cách đây một thời gian về cách ObjC hoạt động để xử lý lỗi: camjuiceliberationfront.com/ Đổi
uliwitness

2
 int result;
 if((result = operation_that_can_throw_ioerror()) == IOError)
 {
  handle_the_exception_somehow();
 }
 else
 {
   # we don't want to catch the IOError if it's raised
   result = another_operation_that_can_throw_ioerror();
 }
 result |= something_we_always_need_to_do();
 return result;

Trong C, bạn sẽ kết thúc với một cái gì đó như ở trên.

Có những điều tôi không thể làm trong Swift mà tôi có thể làm với ngoại lệ không?

Không, không có gì cả. Bạn chỉ cần kết thúc xử lý mã kết quả thay vì ngoại lệ.
Các ngoại lệ cho phép bạn sắp xếp lại mã của mình để việc xử lý lỗi tách biệt với mã đường dẫn hạnh phúc của bạn, nhưng đó là về nó.


Và, tương tự, những cuộc gọi để ...throw_ioerror()trả lại lỗi thay vì ném ngoại lệ?
orome

1
@raxacoricofallapatorius Nếu chúng tôi tuyên bố ngoại lệ không tồn tại thì tôi giả sử chương trình tuân theo mô hình thông thường là trả lại mã lỗi khi thất bại và 0 khi thành công.
ném đá vào

1
@stonemetal Một số ngôn ngữ, như Rust và Haskell, sử dụng hệ thống loại để trả về một cái gì đó có ý nghĩa hơn mã lỗi, mà không thêm các điểm thoát ẩn theo cách mà các trường hợp ngoại lệ thực hiện. Ví dụ, một hàm Rust có thể trả về một Result<T, E>enum, có thể là một Ok<T>hoặc một Err<E>, với Tkiểu bạn muốn nếu có và Elà một kiểu biểu thị một lỗi. Kết hợp mẫu và một số phương pháp cụ thể giúp đơn giản hóa việc xử lý cả thành công và thất bại. Nói tóm lại, đừng cho rằng thiếu ngoại lệ tự động có nghĩa là mã lỗi.
8bittree

1

Ngoài câu trả lời của Charlie:

Những ví dụ về xử lý ngoại lệ được khai báo mà bạn thấy trong nhiều sách hướng dẫn và sách, trông rất thông minh chỉ trên các ví dụ rất nhỏ.

Ngay cả khi bạn gạt bỏ tranh luận về trạng thái đối tượng không hợp lệ, chúng luôn mang đến một nỗi đau thực sự rất lớn khi làm việc với một ứng dụng lớn.

Ví dụ: khi bạn phải xử lý IO, sử dụng một số mật mã, bạn có thể có 20 loại ngoại lệ có thể bị loại bỏ khỏi 50 phương thức. Hãy tưởng tượng số lượng mã xử lý ngoại lệ bạn sẽ cần. Xử lý ngoại lệ sẽ mất nhiều lần hơn mã so với chính mã.

Trong thực tế, bạn biết khi nào ngoại lệ không thể xuất hiện và bạn không bao giờ cần phải viết quá nhiều xử lý ngoại lệ, vì vậy bạn chỉ cần sử dụng một số cách giải quyết để bỏ qua các ngoại lệ được khai báo. Trong thực tế của tôi, chỉ có khoảng 5% trường hợp ngoại lệ được khai báo cần được xử lý trong mã để có một ứng dụng đáng tin cậy.


Vâng, trong thực tế, những ngoại lệ này thường có thể được xử lý chỉ ở một nơi. Ví dụ: trong chức năng "tải xuống dữ liệu cập nhật" nếu SSL bị lỗi hoặc DNS không thể giải quyết được hoặc máy chủ web trả về 404, điều đó không thành vấn đề, hãy bắt nó ở trên cùng và báo cáo lỗi cho người dùng.
Zan Lynx
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.