2012-01-26 32 views
12

Tôi là lập trình viên hư hỏng và cho đến bây giờ tôi chưa xử lý đúng lỗi (ví dụ: chỉ cần bắt một java.lang.Exception, in một thông báo gỡ lỗi và tiếp tục). Khi tôi "xử lý" chúng, nó đơn giản là đóng trình biên dịch lên.Xử lý lỗi - đây có phải là một mẫu khá hay không?

Gần đây tôi đã học được lỗi (haha) theo cách của tôi và muốn bắt đầu thực hiện đúng. Vì vậy, tôi đang nghiên cứu nó ở đây và những nơi khác (thông qua tìm kiếm của Google).

Giả sử tôi có một khối mã mà làm như sau:

... 
x.method1(); // throws ExceptionTypeA 
    ... 
y.method2(); // throws ExceptionTypeB 
    ... 
z.method3(); // throws ExceptionTypeC 
    ... 
x.method4(); // throws ExceptionTypeA (again) 
    ... 

Từ những gì tôi đã thu thập được, cách thích hợp để xử lý này là:

try { 
     ... 
    x.method1(); // throws ExceptionTypeA 
     ... 
    y.method2(); // throws ExceptionTypeB 
     ... 
    z.method3(); // throws ExceptionTypeC 
     ...  
    x.method4(); // throws ExceptionTypeA (again) 
     ... 
} catch (ExceptionTypeA e) { 
    // do something about condition A 
} catch (ExceptionTypeB e) { 
    // do something about condition B 
} catch (ExceptionTypeC e) { 
    // do something about condition C 
} 

này có vẻ khá đơn giản để tôi, nhưng nó có vẻ lộn xộn khi tôi có một khối mã dài mà ném các lỗi khác nhau trong suốt. Tôi dường như gió lên với chỉ một người khổng lồ cố gắng/bắt xung quanh toàn bộ phương pháp của tôi! Cách thay thế có vẻ là:

try { 
     ... 
    x.method1(); // throws ExceptionTypeA 
     ... 
} catch (ExceptionTypeA e) { 
    // do something about condition A 
} 

try { 
     ... 
    y.method2(); // throws ExceptionTypeB 
     ... 
} catch (ExceptionTypeB e) { 
    // do something about condition A 
} 

try { 
     ... 
    z.method3(); // throws ExceptionTypeC 
     ... 
} catch (ExceptionTypeC e) { 
    // do something about condition C 
} 
try { 
     ... 
    x.method4(); // throws ExceptionTypeA 
     ... 
} catch (ExceptionTypeA e) { 
    // do something about condition A 
} 

Điều này có vẻ thực sự khó chịu. Trong những trường hợp như thế này, tôi đã coi là làm một cái gì đó như sau:

private void doSomething() throws exceptionTypeA, exceptionTypeB, exceptionTypeC { 
     ... 
    x.method1(); // throws ExceptionTypeA 
     ... 
    y.method2(); // throws ExceptionTypeB 
     ... 
    z.method3(); // throws ExceptionTypeC 
     ... 
    x.method4(); // throws ExceptionTypeA (again) 
     ... 

} 

public void doSomething_andHandleErrors() { 
    try { 
     this.toSomething(); 
    } catch (ExceptionTypeA e) { 
     // do something about condition A 
    } catch (ExceptionTypeB e) { 
     // do something about condition B 
    } catch (ExceptionTypeC e) { 
     // do something about condition C 
    } 
} 

... và sau đó doSomething_andHandleErrors chỉ cần gọi(); từ bên ngoài. Đây có phải là một thực hành 'tốt' không? Tôi có rơi vào một số mẫu không?

Cảm ơn!

+0

Tôi sẽ chuyển điều này đến http://codereview.stackexchange.com –

Trả lời

8

Sự khác biệt chính giữa ví dụ đầu tiên và thứ hai là cách bạn tự xử lý lỗi. Nó có giao dịch không? Trong ví dụ đầu tiên của bạn, y.method2() sẽ không chạy nếu x.method1() ném một ngoại lệ. Trong ví dụ thứ hai của bạn, có thể tùy thuộc vào việc xử lý lỗi.

Cả hai đều là mẫu phù hợp, đó là vấn đề của trường hợp doanh nghiệp được yêu cầu tại đây. Bạn có muốn ngoại lệ được chuyển đến người gọi để họ có thể xử lý nó không? Bạn có muốn làm điều gì khác vì lỗi này không?

Ngoài ra, đừng quên khối cuối cùng. Bạn sẽ cần phải chắc chắn rằng bạn sử dụng một nếu bạn đang đối phó với quản lý tài nguyên (IO Streams, kết nối cơ sở dữ liệu, ví dụ) để bạn có thể làm sạch nếu cần thiết.

+1

Đồng ý, thứ nhất hoặc thứ hai, tùy thuộc vào việc xử lý mong muốn. Thứ ba, không tốt lắm. – CPerkins

+1

Hoặc thử-với-tài nguyên thay vì 'cuối cùng' khối. – biziclop

+0

Có, nếu bạn đang sử dụng Java 7, bạn có thể dùng try-with-resources thay vì biziclop nói. Bạn không chỉ định phiên bản java bạn đang sử dụng. – Michael

0

Tôi muốn ví dụ đầu tiên của bạn nếu không có nhiều hơn một vài dòng giữa các cuộc gọi phương thức. Nếu không, ví dụ thứ hai của bạn sẽ phù hợp hơn. Tôi đã không bao giờ nhìn thấy các mô hình trong ví dụ cuối cùng, không bao giờ trong codebase của chúng tôi anyway.

1

Thông thường không có quá nhiều thứ khác nhau để thực hiện trong khối đánh bắt, vì vậy bạn nên có một khối cho một hành vi.

Vì vậy, nếu tất cả những gì bạn đang làm là ghi nhật ký và trả lại ngoại lệ, bạn nên chọn tùy chọn đầu tiên. Rất hiếm khi bạn phải xử lý từng trường hợp ngoại lệ có thể, và chọn tùy chọn hai khi không thực sự cần thiết làm giảm khả năng đọc, đặc biệt nếu bạn gán giá trị cho biến cục bộ, bạn phải khai báo và khởi tạo bên ngoài khối try.

boolean success = false; 
try { 
    success = doStuff(); 
} catch(...) { 
    ... 
} 

Đây là mẫu mã khá khủng khiếp và tôi cố gắng tránh nó nếu có thể.Cách để làm điều đó là nhận ra rằng việc xếp các khối catch của bạn (tùy chọn hai) chỉ có ý nghĩa nếu những khối catch chấm dứt bình thường (nghĩa là không có ngoại lệ nào được ném ra khỏi chúng). Nhưng trong trường hợp đó bạn có thể di chuyển toàn bộ khối vào phương thức được gọi.

private boolean doStuff() { 
    try { 
     ...do stuff... 
     return true; 
    } catch(SomeException ex) { 
     ...fidget around... 
     return false; 
    } 
} 

Và bạn chỉ cần gọi nó như thế này:

boolean success = doStuff(); 

Là một sang một bên, Java 7 giúp bạn với ngoại lệ xử lý rất nhiều, bạn có thể catch multiple exceptions in one catch block, or catch and rethrow neatly. Nó cũng giúp bạn để do away with catch blocks altogether cho những thứ như đóng kết nối. Nếu không có yếu tố nào khác khiến bạn trở lại, tôi sẽ cân nhắc đến việc đó.

0

Thật tuyệt khi bạn đang cố gắng tạo mã sạch.

IMHO, những gì bạn đang làm là quá nhẹ. Giả sử bạn phải xử lý các ngoại lệ, tất cả những gì bạn đang làm là tạo một cuộc gọi phương thức khác. Bạn vẫn cần một khối try/catch. Tôi sẽ chỉ làm những gì bạn gọi là "cách thích hợp" để xử lý nó.

Nếu bạn không cần xử lý các ngoại lệ và chúng đại diện cho các lỗi mà bạn không thể khôi phục, bạn có thể tạo ngoại lệ thời gian chạy, sẽ dừng chương trình của bạn (giả sử bạn không bắt chúng).

0

Khối mã cuối cùng bạn có vẻ đẹp, ngoài một thứ. Sẽ tốt hơn nếu các ngoại lệ mở rộng từ RuntimeException. Bằng cách đó bạn không phải báo cáo những ngoại lệ doSomething() ném.

Việc duy trì xử lý lỗi tách biệt với phần còn lại của mã của bạn thường là thực hành tốt. Nó giữ mã khác sạch sẽ.

+0

Bạn có thể vui lòng làm rõ ý của bạn bằng cách "nếu ngoại lệ mở rộng từ RuntimeException" không? – loneboat

+0

Trường hợp ngoại lệ được kiểm tra cần được quảng cáo trên mọi phương pháp có thể được ném vào bên trong, điều này có xu hướng làm lộn xộn mã. Các ngoại lệ thời gian chạy không cần quảng cáo, vì vậy nếu bạn ném một số ngoại lệ tùy chỉnh trong các phương thức của mình, tôi chắc chắn rằng chúng là ngoại lệ thời gian chạy. – fivedigit

+0

Tuyệt vời, cảm ơn!(văn bản phụ) – loneboat

0

cả ví dụ đầu tiên và thứ ba của bạn dường như là cách tốt nhất để xử lý các loại tình huống này, và cách tôi thường xử lý số lượng ngoại lệ đôi khi mã của tôi có thể bị ném. Cá nhân, tôi thích lựa chọn thứ ba, nó được tổ chức và súc tích hơn nhiều, và không rơi vào bất kỳ mẫu chống nào mà tôi biết. Lựa chọn thứ hai chỉ là xấu xí, và nên tránh, có nó chỉ là một sự lãng phí lớn của không gian kể từ khi bạn không cần một số lượng lớn các điều khoản cố gắng làm được mọi việc.

2

Điểm xử lý ngoại lệ là bạn sẽ có thể tiến hành thêm ngay cả khi bạn phải đối mặt với ngoại lệ. Cách thứ nhất và cách thứ ba về cơ bản giống nhau. Nếu ngoại lệ xảy ra trong method1(), bạn sẽ trực tiếp thoát toàn bộ phương thức gốc mà không cần thực hiện method2() và các phương thức khác .. Phương pháp thứ hai có vẻ lộn xộn lúc đầu nhưng thực sự là cách thực hiện.

Thậm chí tốt hơn là xử lý các ngoại lệ mà bạn mong đợi nó ném vào chính phương thức và trả về một loại giá trị mặc định sẽ cho phép thực hiện thêm mà không vi phạm logic nghiệp vụ hoặc không thống nhất.

EDIT:

Ví dụ về lợi thế khi sử dụng 2 phương pháp:

Giả sử bạn đang thực hiện một phân tích cú pháp văn bản và mong đợi một date theo định dạng DD-MM-YYYY. Nhưng trong khi phân tích cú pháp bạn thấy rằng bạn nhận được date ở định dạng DD-MON-YYYY. Các loại ngoại lệ phân tích cú pháp này có thể được xử lý và vẫn cho phép thực hiện thêm.

0

Nếu có một số trường hợp ngoại lệ được ném chỉ một lúc bằng cách chỉ là một khối duy nhất của mã và nếu nơi xử lý ngoại lệ không ảnh hưởng đến logic kinh doanh, tôi thích để xử lý chúng tại chỗ.

Nhưng nếu cùng một ngoại lệ có thể được ném từ nhiều nơi, tôi muốn xử lý nó ở cuối, như cách thích hợp của bạn.

Thêm một cuộc gọi phương thức bổ sung có vẻ hơi vụng về với tôi, bởi vì bạn không tránh được vấn đề, tất cả những gì bạn đang làm là đặt nó ở một nơi khác.

Một điều quan trọng bị thiếu ở đây là cuối cùng là khối, điều mà tôi nghĩ là cần thiết bất kể kiểu xử lý ngoại lệ.

Tất nhiên, đây là lựa chọn cá nhân, tôi đoán là không có câu trả lời đúng hay sai.

1

Điều này thực sự phụ thuộc vào vị trí (trên cấp độ nào) bạn muốn catch ngoại lệ đó.

Phương pháp ném ngoại lệ đơn giản có nghĩa là "Tôi không muốn đối phó với ngoại lệ/vấn đề này, hãy để bất kỳ ai khác bắt" nó. Mã sạch sẽ đến sau cách tư duy này; Tôi có nên bắt tại đây hay không ...

Trong trường hợp cuối cùng nếu bạn ném lại những ngoại lệ đó, điều này có nghĩa là bạn sẽ không cần các đối tượng x, y, z trong quá trình xử lý lỗi vì chúng có thể nằm ngoài phạm vi .

+0

Điểm tốt về phạm vi trong ví dụ cuối cùng. Cảm ơn! – loneboat

1

Điểm ném ngoại lệ là thông báo cho người gọi rằng bạn không thể hoàn thành hành động được yêu cầu, điểm bắt ngoại lệ là thực hiện hành động thích hợp để đáp ứng với lỗi đó. Nếu hành động của bạn không khác nhau dựa trên loại ngoại lệ, thì việc bắt một loại cụ thể là vô dụng.

Là người gọi của một ngoại lệ bị bắt, công việc của bạn hoặc là để phục hồi từ thất bại (thử phương pháp thay thế, sử dụng mặc định, whaever), hoặc để quản lý sự thất bại (dọn dẹp, đăng nhập).

Nếu loại ngoại lệ cũng hữu ích khi thực hiện, hãy lấy loại, nếu bạn không làm, hãy để nó bong bóng đến một trong những người gọi có thể.

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