2010-06-11 28 views
5

tôi thấy mình gặp rất nhiều về điều này trong các phương pháp khác nhau trong mã của tôi:Sử dụng một lớp chung để thực thi với try/catch/finally?

try 
{ 
    runABunchOfMethods(); 
} 
catch (Exception ex) 
{ 
    logger.Log(ex); 
} 

gì về việc tạo này:

public static class Executor 
{ 

    private static ILogger logger; 

    public delegate void ExecuteThis(); 

    static Executor() 
    { 
     // logger = ...GetLoggerFromIoC(); 
    } 

    public static void Execute<T>(ExecuteThis executeThis) 
     where T : Exception 
    { 
     try 
     { 
      executeThis(); 
     } 
     catch (T ex) 
     { 
      // Some kind of Exception Handling Strategy... 
      logger.Log(ex); 
      // throw; 
     } 
    } 

} 

Và chỉ sử dụng nó như thế này:

private void RunSomething() 
{ 
    Method1(someClassVar); 
    Method2(someOtherClassVar); 
} 

...

Executor.Execute<ApplicationException>(RunSomething); 

Có bất kỳ nhược điểm nào đối với phương pháp này không? (Bạn có thể thêm Executor-phương pháp và đại biểu khi bạn muốn có một cuối cùng và sử dụng generics cho các loại Exeception bạn muốn bắt ...)

Edit: Xin lỗi vì không rõ ràng - những gì tôi đã thực sự sau là một số đầu vào vào ý tưởng chung của việc cố gắng di chuyển việc thực thi mã từ lớp được đề cập đến một lớp tổng quát hơn thực hiện điều này. Tôi chỉ thực hiện một giải pháp nhanh chóng nhưng thực tế bạn sẽ sử dụng những thứ như chiến lược xử lý ngoại lệ, các lớp cơ sở thực thi trừu tượng với các lớp thực thi chuyên biệt hơn cho một lớp/phần cụ thể của hệ thống. Tôi thường tạo một phương thức với try .../runABunchOfMethods-part (điều này xử lý ngoại lệ, với các ngoại lệ đặc biệt) gọi runABunchOfMethods để thực thi một tập hợp hạn chế các kiểu "clean code" khác.

Tôi sẽ mua đối số obfuscation ở một số cấp nhưng nếu toàn bộ giải pháp/kiến ​​trúc thực hiện phương pháp được đề xuất này thì lập trình mới sẽ có thể hiểu được mẫu.

Tôi đã chỉnh sửa Executor để bao gồm một T chung để cho phép mã gọi để chỉ định các exeception chỉ để hiển thị như thế nào để xử lý một exeception chuyên ngành. Trong các trường hợp khác, bạn có thể có một loạt các catch: es tùy thuộc vào những gì bạn muốn làm nhưng đó là những trường hợp đặc biệt trong các lớp con cụ thể tôi đã nói về.

Trả lời

2

Nếu bạn muốn giữ cho các đối tượng của mình sạch sẽ, bạn có thể xem xét sử dụng khung AOP như PostSharp. Sau đó, bạn đăng nhập ngoại lệ (ví dụ) tất cả có thể được xử lý ở một nơi nếu bạn muốn.

EDIT:

Có thể để loại bỏ các khối try/catch sử dụng PostSharp - đây là một ví dụ phổ biến xử lý ngoại lệ có thể được tạo ra trong PostSharp:

[Serializable] 
public class CommonExceptionHandling : OnExceptionAspect 
{ 
    public override void OnException(MethodExecutionEventArgs eventArgs) 
    { 
     // can do some logging here 
     // ... 

     // throw the exception (out of postsharp) to where it occurred: 
     eventArgs.FlowBehavior = FlowBehavior.RethrowException;      

     // If you want to ignore the exception, you can do this: 
     //eventArgs.FlowBehavior = FlowBehavior.Return; 

     base.OnException(eventArgs); 
    } 
} 

Nếu bạn áp dụng thuộc tính này để một lớp, bất kỳ ngoại lệ nào mà bất kỳ phương thức nào trong lớp đó sẽ được ném qua đoạn mã trên.

+0

Đẹp! Đó là chính xác những gì tôi đang tìm kiếm - và sử dụng các thuộc tính trên các phương thức của bạn đẹp hơn nhiều so với Executer.Execute (delegate) -stuff. Có vẻ như tôi không xa câu trả lời như mọi người nghĩ ... :) Ai cũng biết về bất kỳ Khung AOP nào khác có hỗ trợ Xử lý ngoại lệ theo phong cách AOP? (Để so sánh ...) – antirysm

+0

Hoặc, thay vì PostSharp, sử dụng khung tiêm phụ thuộc, hầu hết trong số đó cho phép chặn động các cuộc gọi phương thức. –

1

Nhược điểm sẽ thiếu tính linh hoạt. Điều gì sẽ xảy ra nếu bạn muốn khôi phục từ một ngoại lệ cụ thể trong một trong các phương pháp được gọi - ví dụ: trên kết nối không thành công với máy chủ SMTP, bạn có thể muốn lưu trữ nội dung của thư đi trong cơ sở dữ liệu của bạn để thử lại sau? Đây là lý do tại sao bạn nên tránh bắt loại ngoại lệ chung bất cứ khi nào có thể.

Ngoài ra, bạn cũng mất một số mã rõ ràng của mình (theo ý kiến ​​của tôi) vì khó có thể biết được xử lý ngoại lệ xảy ra ở đâu.

12

Downsite là cách tiếp cận xử lý ngoại lệ chung của bạn. Bạn không nên bắt baseclass Exception mà không có một lý do mạnh mẽ. Nó có thể che giấu nhiều vấn đề khác nhau; ok, bạn đăng nhập chúng, nhưng mã của bạn không biết rằng nó chỉ chạy hết bộ nhớ hoặc tệp không tồn tại và tiếp tục như thể không có vấn đề gì cả.
Phương pháp bạn mô tả ở đây sẽ khuyến khích xử lý ngoại lệ chung.

liên quan:
Is it really that bad to catch a general exception?
Is this a bad practice to catch a non-specific exception such as System.Exception? Why?

EDIT (phản ứng để chỉnh sửa & làm rõ của OP):
Tôi không chắc chắn những gì bạn muốn đạt được. Basicall bạn ẩn một ngoại lệ (chung hoặc chỉ định - nó chỉ là nuốt). Gọi phương thức của bạn Executor.Hide<ApplicationException>(RunSomething); và rõ ràng điều gì xảy ra ở đây. Nhưng có lợi thế nào trong việc nuốt ngoại lệ không? Tôi không nghĩ vậy. Một lần nữa, có thể có những nơi bạn cần điều này - nhưng chúng rất hiếm và nên được chọn một cách có chủ ý. Phương pháp bạn cung cấp khuyến khích nuốt ngoại lệ mà không suy nghĩ về nó.
Bạn đã nhận xét đường dây trả lại (throw ex hoặc tốt hơn chỉ throw, trong đó giữ nguyên ngăn xếp). Bạn nhận được gì với dòng này được bật? Về cơ bản chỉ cần đăng nhập. Bạn bắt một ngoại lệ, đăng nhập nó và rethrow nó để ... bắt nó một lần nữa? Tại sao bạn không thể đăng nhập vào địa điểm sau này?
Cố gắng bắt ngoại lệ, khi bạn có thể xử lý nó. Ở đó bạn cũng có thể đăng nhập. Bất kỳ trình ghi nhật ký nào cũng sẽ có thể hiển thị cho bạn dấu vết ngăn xếp để bạn không bị mất thông tin.

liên quan:
Where to put try catch?

+0

+1 nếu có thể, tôi sẽ upvote nhiều lần. – Jehof

+0

Tôi đã chỉnh sửa mã để sử dụng Generics và vượt qua trong một T: ngoại lệ để cho thấy rằng bạn có thể dễ dàng chỉ định một ngoại lệ chuyên biệt. Vui lòng xem chỉnh sửa của tôi. – antirysm

+0

@antirysm: Tôi đã cập nhật câu trả lời của mình ... – tanascius

3

Bạn nhận được một mức gián tiếp mà có thể làm cho mã của bạn khó khăn hơn để đọc và hiểu (nhất là đối với một người nào đó nhìn vào mã của bạn cho lần đầu tiên).

+1

Tôi không chắc chắn, mã với một loạt các xử lý ngoại lệ trong dòng không phải là dễ đọc. Và trong trường hợp này, đó là một mô hình khá trực quan đang được trình bày. –

1

Nếu bạn thấy mã đó trong nhiều lớp thì bạn đang làm điều gì đó rất sai trong sách của mình :-) Tại ít nhất bạn nên ném lại ngoại lệ (chỉ với "ném", không phải "ném (cũ)"), nhưng toàn bộ ý tưởng bắt ngoại lệ cơ bản tất cả thời gian chỉ để đăng nhập nó (hoặc thực sự tại bất kỳ thời gian, thanh một vài trường hợp cạnh) không phải là cách tiếp cận phù hợp với tôi.

Tôi không biết bạn đang viết loại ứng dụng nào, nhưng bạn không thể móc các sự kiện như AppDomain.UnhandledException, sẽ kích hoạt bất cứ khi nào có ngoại lệ chưa được xử lý (một trong những bạn chưa nắm bắt) trong bất kỳ chủ đề nào và đăng nhập ? Bạn có thể muốn kết hợp điều đó với các câu lệnh khai thác cụ thể hơn cũng ghi lại, nếu bạn muốn ghi lại các ngoại lệ có thể phục hồi được.

+0

Yup, exeception ex chỉ là một minh họa (mặc dù một xấu). Chúng ta đang nói về các phương thức ở mức rất thấp, nơi chỉ có một số trường hợp ngoại lệ hạn chế nằm trong phạm vi. – antirysm

1

Tôi sẽ nhảy vào "nó khiến bạn có khả năng lạm dụng ngoại lệ bằng cách chỉ bắt ở mức chung" bandwagon. Tuy nhiên, bạn có thể hưởng lợi từ một số loại 'ngoại lệ xử lý ủy nhiệm wrapper' trong trường hợp đặc biệt hơn. Ví dụ, nếu bạn đã làm rất nhiều công việc với các lớp liên quan đến DB mà luôn ném cùng một ngoại lệ, bạn có thể có một trình bao bọc-ngoại lệ DB chuyên dụng.

Cuối cùng, thực tế là ngoại lệ 'xáo trộn' mã của bạn là nhược điểm để làm cho mã của bạn an toàn.

+0

+1 Cảm ơn bạn đã hiểu mục đích của câu hỏi của tôi! :) Xin vui lòng xem chỉnh sửa của tôi! – antirysm

2

Bạn không thực sự giành chiến thắng bất cứ điều gì, nhưng bạn mất một số điều:

  • Độ khó
  • đơn giản (bạn giới thiệu lớp khác, một đại biểu mà một người đọc phải hiểu)
  • Bạn có thể không chỉ cần bước qua Executor.Execute để đến Method1 (...). Nếu bạn bước qua, bạn bỏ qua toàn bộ khối mã trong ủy nhiệm của bạn.
  • ...
+0

+1 cho điểm gỡ lỗi - đó là một mối quan tâm thực sự hợp lệ! Cảm ơn! – antirysm

0

Khi bắt đầu rất ....

Bạn không nên có một mã như dưới đây nằm rải rác xung quanh trong cơ sở mã của bạn.

try 
{ 
    runABunchOfMethods(); 
} 
catch (Exception ex) 
{ 
    logger.Log(ex); 
} 

Tôi khuyên bạn trước hết nên xem xét nhu cầu thực tế của mã như thế này. Bạn nên bắt một ngoại lệ rất cụ thể trong tất cả các trường hợp (không giống như bắt tất cả).

Sau đó, nếu bạn vẫn còn nhiều phương thức bắt cùng một ngoại lệ thì bạn có thể nghĩ đến việc tạo nhiều phương thức trong trình xử lý của bạn xử lý một loại ngoại lệ cụ thể.

+0

Tôi nên rõ ràng hơn; mã ở trên sẽ là mã duy nhất trong một hàm; runABunchOfMethods() sẽ chứa các cuộc gọi đến tập hạn chế các chức năng khác. "Clean Code"/Chú Bob-style ... – antirysm

1

Thông thường mô hình tốt nhất cho trường hợp ngoại lệ khai thác gỗ trong ứng dụng của bạn là để chỉ ngoại lệ bắt tại top-level phương pháp trong mã của bạn.

  • Trong ứng dụng bảng điều khiển, bạn nên nắm bắt và ghi lại ngoại lệ trong Main.
  • Trong ứng dụng WinForms, bạn nên nắm bắt và ghi lại các ngoại lệ trong trình xử lý sự kiện của mình.
  • Trong dịch vụ, bạn nên bắt và ghi lại ngoại lệ, sau đó là ThreadProcs của chuỗi công việc của bạn.
  • vv

Tại hầu hết các điểm khác trong ứng dụng của bạn, bạn chỉ nên bắt ngoại lệ cụ thể và sau đó rethrow một ngoại lệ mới gói ngoại lệ ban đầu. Nếu bạn không cần phải bọc ngoại lệ ban đầu, bạn không cần phải nắm bắt ngoại lệ ban đầu ở nơi đầu tiên.

Nhập đơn của bạn như thế này sẽ dẫn đến chỉ một vài nơi bạn bắt, đăng nhập và "nuốt" ngoại lệ.

+0

Yup, tôi đã chỉnh sửa câu hỏi để cố gắng giải thích ý định của mình tốt hơn một chút. Mối quan tâm của tôi là tôi muốn giữ các đối tượng "thật" của mình càng sạch càng tốt. Trong ví dụ của tôi, tôi đã sử dụng Logging làm ví dụ nhưng tôi cũng nghĩ về các khía cạnh khác nhau (chẳng hạn như kiểm tra, truy tìm v.v.) mà tôi không muốn làm lộn xộn mã bình thường của mình. Tôi thấy rằng nhiều mô hình là như nhau vì vậy tôi muốn sử dụng lại nó một cách tổng quát hơn trong khi vẫn cho phép chuyên môn hóa. – antirysm

+1

@antirysm: Điểm của Martin ở đây là bạn * có thể * giữ cho "đối tượng thực sự" của bạn sạch từ các câu lệnh try/catch bằng cách * không * đặt câu lệnh try/catch vào trong chúng. Cho phép chúng bong bóng lên đầu ứng dụng của bạn và đăng nhập chúng ở đó. –

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