Ném và bắt ngoại lệ trong cùng một chức năng / phương pháp


10

Tôi đã viết một hàm yêu cầu người dùng nhập liệu cho đến khi người dùng nhập số nguyên dương (số tự nhiên). Ai đó nói rằng tôi không nên ném và bắt ngoại lệ trong chức năng của mình và nên để người gọi chức năng của tôi xử lý chúng.

Tôi tự hỏi những nhà phát triển khác nghĩ gì về điều này. Tôi cũng có thể lạm dụng các ngoại lệ trong chức năng. Đây là mã trong Java:

private static int sideInput()
{
    int side = 0;
    String input;
    Scanner scanner = new Scanner(System.in);

    do {
        System.out.print("Side length: ");
        input = scanner.nextLine();
        try {
            side = Integer.parseInt(input);
            if (side <= 0) {
                // probably a misuse of exceptions
                throw new NumberFormatException();
            }
        }
        catch (NumberFormatException numFormExc) {
            System.out.println("Invalid input. Enter a natural number.");
        }
    } while (side <= 0);

    return side;
}

Tôi quan tâm đến hai điều:

  1. Tôi có nên để người gọi lo lắng về ngoại lệ? Điểm của chức năng là nó cằn nhằn người dùng cho đến khi người dùng nhập số tự nhiên. Là điểm của chức năng xấu? Tôi không nói về UI (người dùng không thể thoát khỏi vòng lặp mà không có đầu vào thích hợp), nhưng về đầu vào bị lặp với các ngoại lệ được xử lý.
  2. Bạn có thể nói tuyên bố ném (trong trường hợp này) là lạm dụng các ngoại lệ? Tôi có thể dễ dàng tạo một cờ để kiểm tra tính hợp lệ của số và xuất thông báo cảnh báo dựa trên cờ đó. Nhưng điều đó sẽ thêm nhiều dòng vào mã và tôi nghĩ nó hoàn toàn có thể đọc được.

Có điều là tôi thường viết một hàm đầu vào riêng biệt. Nếu người dùng phải nhập một số nhiều lần, tôi tạo một hàm riêng cho đầu vào xử lý tất cả các ngoại lệ và giới hạn định dạng.


Rất phụ thuộc vào ngôn ngữ. Một số ngôn ngữ sử dụng ngoại lệ một cách tự do hơn những ngôn ngữ khác.
Martin York

Câu trả lời:


11

Điểm của một trường hợp ngoại lệ là nó cho phép một phương thức cho biết người gọi đó đã vào trạng thái không thể tiếp tục bình thường mà không buộc bạn phải nhúng mã lỗi vào giá trị trả về.

Trong trường hợp của bạn, phương thức của bạn biết chính xác phải làm gì khi đầu vào không lớn hơn 0. Lý do duy nhất bạn lưu dòng ở đây là do bạn tình cờ ném ngoại lệ giống như bạn sẽ nhận được nếu đầu vào không phải là số. Tuy nhiên, ngoại lệ bạn đang ném không thể hiện đúng lý do tại sao mã của bạn không thích đầu vào. Nếu có người khác đi cùng và xem mã này, họ sẽ phải dành thêm thời gian để cố gắng xem chính xác mọi thứ đang hoạt động như thế nào.


Vâng, đó là lý do tại sao tôi nghĩ rằng đó là một lạm dụng. Vì vậy, bỏ qua câu lệnh ném đó, bạn có đồng ý rằng xử lý các ngoại lệ trong vòng lặp như vậy bên trong một hàm thay vì để người gọi bắt chúng không? Câu lệnh bắt là có vì Integer.parseInt (một lần nữa, bỏ qua cú ném).
usr

1
@usr: Câu trả lời đó phụ thuộc nhiều vào cách phần còn lại của hệ thống có nghĩa là làm việc cùng nhau. Trường hợp ngoại lệ cần phải được xử lý tại một số điểm. Có một vài cách khác nhau bạn có thể tổ chức nó và đây là một trong số đó. Cái nào tốt nhất phụ thuộc vào thông tin khác mà chúng tôi không có ở đây.
unolysampler

4

Đây là một sử dụng xấu của ngoại lệ. Để bắt đầu, một số không tích cực không phải là một ngoại lệ định dạng.

Tại sao sử dụng ngoại lệ ở tất cả? Nếu bạn biết đầu vào nào không được phép, chỉ cần không thoát ra khỏi vòng lặp cho đến khi bạn nhận được đầu vào hợp lệ từ người dùng, đại loại như sau:

while (true)
{
   // Get user input.
   String input = scanner.nextLine();

   try
   {
      side = Integer.parseInt(input);

      break;
   }
   catch (NumberFormatException ex)
   {
      // Inform user of invalid input.
      System.out.println("Invalid input. Enter a natural number.");
   }
}

Integer.intPude đưa ra một ngoại lệ định dạng, vì vậy sử dụng câu lệnh bắt là hoàn toàn hợp lệ. Tôi chủ yếu muốn biết liệu có ổn không khi sử dụng câu lệnh bắt trong một vòng lặp trong hàm hoặc tôi nên để người gọi của hàm xử lý ngoại lệ định dạng.
usr

Integer.parseInt()ném một NumberFormatExceptionnếu nó không thể phân tích đối số chuỗi được cung cấp. Bạn không cần (và bạn sẽ không thể) ném ngoại lệ cho mình.
Bernard

Tôi đã chỉnh sửa ví dụ mã để rõ ràng hơn.
Bernard

"và bạn sẽ không thể) tự ném ngoại lệ" - ý bạn là gì ở đó?
Michael Borgwardt

@Michael Borgwardt: Ý tôi là vì Integer.parseInt()phương pháp sẽ ném ngoại lệ cho bạn nếu nó xảy ra, bạn sẽ không thể tự ném nó sau đó vì nó đã bị ném.
Bernard

1

Chỉ bắt ngoại lệ nếu bạn định làm điều gì đó có liên quan đến lệnh gọi phương thức hiện tại; tức là dọn dẹp, logic thất bại, v.v.

Người ta có thể thoát khỏi thử / bắt ở đây và chỉ cần ghi lại cuộc gọi phương thức:

//Throws NumberFormatException if read input is less than 0
private static int sideInput()

Người ta vẫn cần xử lý ngoại lệ đó hơn nữa trong chuỗi cuộc gọi / ngăn xếp!


1
Thông báo là có cho một giao diện người dùng tốt hơn. Điểm quan trọng của chức năng là nó cằn nhằn người dùng cho đầu vào cho đến khi họ nhập một đầu vào hợp lệ. Điều này không thể được thực hiện mà không có khối bắt thử. Câu hỏi của tôi là nếu điểm chính nó là hợp lệ. Tôi nghĩ là vậy, nhưng ai đó nói với tôi rằng tôi nên loại bỏ các khối thử và để người gọi xử lý ngoại lệ đặc biệt này. Nhưng sau đó chức năng sẽ không hoạt động như dự định.
usr

1

Bạn không nên ném và bắt cùng một ngoại lệ trong một phương thức, tôi thậm chí nghĩ rằng khối bắt sẽ bắt được ngoại lệ giống như bạn đang ném, vì vậy bạn không thực sự ném nó.

Nếu parseIntthành công, thì đó không phải là a NumberFormatException.

nếu bên nhỏ hơn 0, bạn nên ném a NegativeSideLengthException;

Tạo một ngoại lệ tùy chỉnh / kinh doanh có tên NegativeSideLengthException

public class NegativeSideLengthException extends Exception
{


    public NegativeSideLengthException(Integer i)
    {
        super("Invalid negative side length "+i);        
    }

}

Sau đó sideInputném NegativeSideL wavelException

private static int sideInput() throws NegativeSideLengthException
{
    int side = 0;
    String input;
    Scanner scanner = new Scanner(System.in);

    do {
        System.out.print("Side length: ");
        input = scanner.nextLine();
        try {
            side = Integer.parseInt(input);
            if (side <= 0) {
                throw new NegativeSideLengthException(side);
            }
        }
        catch (NumberFormatException numFormExc) {
            System.out.println("Invalid input. Enter a natural number.");
        }
    } while (side <= 0);

    return side;
}

Bạn thậm chí có thể (nếu bạn muốn) thêm một khối bắt khác để bắt NegativeSideLengthExceptionvà không có phương thức ném nó.

do {
    System.out.print("Side length: ");
    input = scanner.nextLine();
    try {
        side = Integer.parseInt(input);
        if (side <= 0) {
            throw new NegativeSideLengthException(side);
        }
    }
    catch (NumberFormatException numFormExc) {
        System.out.println("Invalid input. Enter a natural number.");
    } catch (NegativeSideLengthException e){
        System.out.println("Invalid input. Enter a non-negative number.");
    }
} while (side <= 0);

Cờ không phải là một cách tốt để xử lý các trường hợp ngoại lệ.


-1

Ngoại lệ là những thứ khá ác mộng, chúng mang lại nhiều phức tạp hơn những gì chúng giải quyết.

Thứ nhất, nếu bạn không nắm bắt được ngoại lệ của mình, người gọi chỉ có thể thực hiện on error resume next, nghĩa là sau một tuần, thậm chí bạn sẽ không biết chức năng của mình có thể ném gì và phải làm gì với nó:

{
    ...
}
catch(OutOfMemory, CorruptedMemory, BadData, DanglingPointers, UnfinishedCommit)
{
    Console.WriteLine("Nothing to see here, move on.");
    Console.WriteLine("The app is very stable, see, no crashing!");
}

Về cơ bản, nếu bạn bắt được chúng, bạn phải hiểu rất rõ về hợp đồng và đảm bảo ngoại lệ. Điều đó hiếm khi xảy ra trong một thế giới thực. Và mã của bạn cũng sẽ khó đọc.

Ngoài ra, điều buồn cười là nếu bạn thực sự có bất kỳ cơ hội xử lý ngoại lệ nào, bạn cần ngôn ngữ RAII thực sự, đó là loại hài hước, vì Java và .NET là tất cả về ngoại lệ ...

Lặp lại một lần nữa, nhưng ...:

http://bloss.msdn.com/b/oldnewthing/archive/2004/04/22/118161.aspx

http://bloss.msdn.com/b/oldnewthing/archive/2005/01/14/352949.aspx

http://www.joelonsoftware.com/items/2003/10/13.html


4
-1 để đăng một câu chuyện biên giới, đầy đủ không chính xác - hoặc ít nhất là phụ thuộc vào ngữ cảnh / ngôn ngữ - thay vì trả lời câu hỏi thực tế của OP.
Péter Török

@ PéterTörök: "Hãy cho một người đàn ông một con cá và bạn nuôi sống anh ta một ngày. Dạy một người đàn ông câu cá và bạn nuôi sống anh ta cả đời." Có những vấn đề rất nghiêm trọng đằng sau các ngoại lệ và mọi người nên biết chúng -1000 / + 1, tôi không thực sự quan tâm.
Coder

1
Hãy tin rằng bạn có thể viết mã chính xác dễ dàng hơn mà không có ngoại lệ. Chỉ không nêu quan điểm và niềm tin của bạn là sự thật (ngay cả khi Joel có cùng quan điểm, đó vẫn là một ý kiến, không phải là sự thật) và không đăng các câu trả lời không liên quan trên SE.
Péter Török

@ PéterTörök: Không phải là ý kiến, đó là sự thật, mỗi khi bạn sử dụng một thành phần ném ngoại lệ bên trong, bạn phải thu thập dữ liệu toàn bộ phân cấp và kiểm tra từng dòng mã để biết phải bắt gì và nếu các thành phần đó cung cấp mạnh đảm bảo, và mọi thứ được khôi phục, hoặc nếu việc bắt đó chỉ là một cảm giác an toàn sai lầm. Heck, bạn thậm chí không biết tất cả các ngoại lệ std :: chuỗi ném, bạn có thể tra cứu thông số kỹ thuật, nhưng bạn sẽ không bao giờ tìm thấy. Những thứ như : throw(thisexception, thatexception)là hoàn toàn sai và không bao giờ nên được sử dụng, bởi vì nếu không, bạn sẽ nhận được bất ngờ.
Coder

2
OK, vậy làm thế nào về mã không sử dụng ngoại lệ? Gee, bạn cần thu thập dữ liệu thông qua mã để kiểm tra từng dòng đơn để xem giá trị trả về được xử lý đúng hay bỏ qua. Và khi giá trị trả về bị bỏ qua, sẽ có thông báo cho đến khi có thể ứng dụng của bạn gặp sự cố vài nghìn dòng sau đó. Ngoại lệ ít nhất buộc bạn phải chú ý. Vâng, bạn có thể nuốt chúng - nhưng chỉ với một cách rõ ràng catch. Mặc dù giá trị trả về bị bỏ qua là không thể tìm kiếm - nó chỉ có thể được xác định thông qua xem xét mã theo từng dòng. Cuối cùng nhưng không kém phần quan trọng, các hàm tạo không có giá trị trả về, đây là lý do quan trọng nhất để sử dụng các ngoại lệ.
Péter Török
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.