Bạn có ném một đối số hoặc đối số từ các phương thức riêng tư?


20

Tôi vừa xem lại một số mã mà tôi đã viết một lúc trước và có thể thấy rằng tôi có một vài phương thức riêng tư đưa ra các tham số đối số và / hoặc đối số nếu có vấn đề với các tham số của phương thức.

Tôi đoán lý do của tôi là giúp chứng minh ứng dụng trong tương lai nếu ai đó cố gắng "lạm dụng" phương pháp trong tương lai. Tuy nhiên, do nó là một phương thức riêng tư và những người có khả năng gọi phương thức này có thể thấy các bình luận và mã liên quan, việc ném nó là không cần thiết. Chắc chắn không có gì đau đớn khi có chúng, mặc dù nó thêm lộn xộn.

Cảm giác của tôi là những ngoại lệ này thường hữu ích hơn đối với một thứ như API sẽ được phơi bày công khai.

Câu trả lời:


22

Thông thường, đối với các phương thức riêng tư mà bạn không đưa ra ngoại lệ vì khi bạn viết nó, nhà phát triển có nghĩa vụ phải biết cách thức và nơi anh ta gọi phương thức đó từ đâu. Như vậy, các biến được truyền dưới dạng tham số cho phương thức riêng nên được kiểm tra bên ngoài phương thức, nghĩa là trước khi gọi nó. Ném "UnlimitedArgumentException" và các ngoại lệ khác như vậy được coi là thông lệ tốt cho các phương thức công khai (cho dù bạn có đang viết "API" hay không).

Đối với những trường hợp bạn muốn ném "UnlimitedArgumentException", điều đáng nói là có một Assertlớp trong API mùa xuân cho Java kể từ phiên bản 1.1.2. Nó rất hữu ích - với tôi ít nhất - bằng cách viết ít mã hơn để thực hiện kiểm tra.

Tuy nhiên, bạn có thể sử dụng "khẳng định" để kiểm tra các tham số trong các phương thức riêng tư. Đó là một trong những mục đích thực sự của họ. Chúng có nhiều lý do hơn để sử dụng chúng, hãy kiểm tra liên kết sau đây cũng giải thích cặn kẽ khi nào nên sử dụng khẳng định và khi nào nên sử dụng ngoại lệ. Các xác nhận không được đưa vào mã sản xuất và trình biên dịch sẽ loại bỏ chúng theo mặc định. Vì vậy, họ là những gì bạn đang tìm kiếm: giúp đỡ các nhà phát triển, vô hình cho người dùng. Trong Java, bạn phải sử dụng một cờ đặc biệt ("-ea") để báo cho trình biên dịch kích hoạt các xác nhận. Bạn có thể coi họ là những người bạn "gỡ lỗi".

Dưới đây là cách sử dụng các xác nhận trong:


1
Apache Commons cũng có một lớp Xác thực hoạt động tương tự như lớp Assert của Spring
Rosa Richter

@cantido có và tôi cũng sẽ thêm rằng lớp Điều kiện tiên quyết trong Guava của Google hoạt động theo cách tương tự. Cảm ơn thông tin :-)
Jalayn 6/10/2015

2

Giống như mọi thứ khác, nó phụ thuộc ....

Nếu các phương thức công khai là các hàm bao đơn giản gọi phương thức riêng (dọc theo dòng của phương thức quá tải riêng) thì có thể có ý nghĩa để ném một ngoại lệ trong phương thức riêng thay vì kiểm tra từng phương thức chung.

Nói chung nếu nó không đáp ứng định nghĩa trên thì tôi thường sẽ không kiểm tra các đối số / ném ngoại lệ vào một phương thức riêng tư. Mặc dù có những trường hợp khác, tôi thường làm điều này trong một phương thức riêng tư trước khi thực hiện một số thao tác đắt tiền có thể thất bại một phần nếu các đối số không hợp lệ.


2

Tôi nhận ra rằng trong khi câu hỏi không có thẻ ngôn ngữ, có lẽ nó đang ngầm nói về ngôn ngữ cà phê của cà phê. Nhưng chỉ để hoàn thiện, tôi muốn đề cập đến sự đồng thuận rõ ràng có phần khác biệt trong thế giới C ++.

Có ba điều mà các lập trình viên C ++ thường sẽ quan tâm:

  • Nó sẽ có chi phí không trong các bản dựng được tối ưu hóa? (Có nghĩa là, nó có thể được biên soạn ra ra không?)
  • Tôi có thể sử dụng nó để bẫy vào trình gỡ lỗi ngay tại điểm phát hiện lỗi không?
  • Tôi có thể sử dụng nó để báo cáo các vấn đề từ các chức năng được khai báo noexceptkhông?

Trước đây, tôi đã tiếp cận vấn đề đầu tiên bằng cách viết mã như thế này

int
factorial(const int n)
{
  if (CHECK_ARGS)
    {
      if (n < 0)
        throw std::invalid_argument {"n < 0"};
    }
  int fac = 1;
  for (int i = 2; i <= n; ++i)
    fac *= i;
  return fac;
}

trong đó d CHECK_ARGS#definehằng số thời gian biên dịch để trình biên dịch có thể loại bỏ hoàn toàn tất cả mã kiểm tra đối số trong các bản dựng được tối ưu hóa. (Tôi không nói rằng việc biên dịch séc là một điều tốt nói chung nhưng tôi tin rằng người dùng nên có tùy chọn để biên dịch chúng.)

Tôi vẫn thích về giải pháp này rằng mã kiểm tra đối số được hiển thị rõ ràng được nhóm lại với nhau thành if. Tuy nhiên, vấn đề thứ hai và thứ ba không được giải quyết bằng cách này. Do đó, giờ đây tôi lại nghiêng về việc sử dụng assertmacro để kiểm tra đối số.

Các tiêu chuẩn mã hóa Boost đồng ý với điều này:

Lỗi lập trình viên thì sao?

Là một nhà phát triển, nếu tôi đã vi phạm điều kiện tiên quyết của thư viện tôi đang sử dụng, tôi không muốn ngăn xếp ngăn xếp. Những gì tôi muốn là một bãi chứa cốt lõi hoặc tương đương - một cách để kiểm tra trạng thái của chương trình tại điểm chính xác nơi phát hiện sự cố. Điều đó thường có nghĩa assert()hoặc một cái gì đó giống như nó.

Có một cuộc nói chuyện rất thú vị được đưa ra bởi John Lakos tại CppCon'14 có tiêu đề Lập trình phòng thủ được thực hiện đúng ( phần 1 , phần 2 ). Trong phần đầu tiên của bài nói chuyện, ông thảo luận về lý thuyết hợp đồng và hành vi không xác định. Trong phần thứ hai, ông trình bày những gì tôi cho là một đề xuất rất tốt để kiểm tra lập luận có hệ thống. Về bản chất, ông đề xuất các macro xác nhận cho phép người dùng chọn bao nhiêu ngân sách (về mức độ sử dụng CPU) mà cô sẵn sàng quyên góp cho thư viện để kiểm tra đối số và thư viện sử dụng ngân sách đó một cách khôn ngoan. Ngoài ra, người dùng cũng có thể cài đặt chức năng xử lý lỗi toàn cầu sẽ được gọi trong trường hợp phát hiện hợp đồng bị hỏng.

Về khía cạnh của một chức năng là riêng tư, tôi không nghĩ rằng điều này có nghĩa là chúng ta không bao giờ nên kiểm tra các đối số của nó. Chúng tôi có thể tin tưởng mã riêng của mình nhiều hơn để không vi phạm hợp đồng của một chức năng nội bộ nhưng chúng tôi cũng biết rằng chúng tôi cũng không hoàn hảo. Kiểm tra đối số trong các chức năng nội bộ cũng hữu ích trong việc phát hiện các lỗi của chúng ta cũng như trong các chức năng công cộng để phát hiện các lỗi trong mã máy khách.


1

Hãy xem xét cấu trúc sau:

  1. Logic bên trong: Hàm này giả sử được gọi với các tham số chính xác và do đó sử dụng các xác nhận để xác minh các điều kiện tiên quyết, hậu điều kiện và bất biến để kiểm tra logic bên trong của bạn.

  2. Giao diện người dùng wrapper: Chức năng này kết thúc tốt đẹp các chức năng nội bộ và sử dụng InvalidArgumentExceptions để xử lý các giá trị sai lầm và để cho người sử dụng để sửa chữa đầu vào của mình: Assert(x).hasLength(4);, Assume(y).isAlphanumeric();, Assert(z).isZipCode();, Assume(mailAdress).matchesRegex(regex_MailAdress);, Reject(x).ifEmpty();,, vv

  3. Trình bao bọc giao diện hàng loạt: Chức năng này bao bọc chức năng bên trong và sử dụng ghi nhật ký, đánh dấu hiệu lực và thống kê để xử lý các giá trị sai mà không làm gián đoạn một số tác vụ chạy dài. Các đánh dấu có thể được sử dụng sau này bởi một người nào đó kiểm tra và làm sạch cơ sở dữ liệu kết quả.

  4. Trình bao bọc giao diện dòng lệnh: chức năng này bao bọc chức năng bên trong và hỏi lại cho đầu vào cuối cùng.

Bạn nên sử dụng cả hai - khẳng định và ngoại lệ - trong các phương pháp khác nhau cho các nhiệm vụ khác nhau. Bạn nên tách logic nội bộ khỏi kiểm tra tham số. So sánh nó với sự tách biệt của Model, View, Controller.


0

Có nhiều cách tốt hơn để tránh kiểm tra tham chiếu null: sử dụng hợp đồng mã hoặc khung AOP để thực hiện kiểm tra cho bạn. Google "hợp đồng mã c" hoặc "postsharp".


Tôi đoán câu hỏi của tôi sẽ mở rộng hợp đồng mã. Có cần kiểm tra các điều kiện tiên quyết của phương pháp trong một phương thức riêng tư (ví dụ: để kiểm chứng trong tương lai hoặc ngăn bạn tự bắn vào chân mình) không?
Ông Moose
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.