Bạn có nên khẳng định không null với câu lệnh khẳng định trong mã sản xuất? [đóng cửa]


32

Tôi đã thấy câu hỏi này nhưng có thêm một vài câu hỏi về việc sử dụng asserttừ khóa. Tôi đã tranh luận với một vài lập trình viên khác về việc sử dụng assert. Đối với trường hợp sử dụng này, có một phương thức có thể trả về null nếu đáp ứng các điều kiện tiên quyết nhất định. Mã tôi đã viết gọi phương thức, sau đó xác nhận nó không trả về null và tiếp tục sử dụng đối tượng được trả về.

Thí dụ:

class CustomObject {
    private Object object;

    @Nullable
    public Object getObject() {
        return (object == null) ? generateObject() : object;
    }
}

Bây giờ hãy tưởng tượng tôi sử dụng nó như thế này:

public void useObject(CustomObject customObject) {
    object = customObject.getObject();
    assert object != null;
    // Do stuff using object, which would throw a NPE if object is null.
}

Tôi đã nói rằng tôi nên loại bỏ assert, rằng chúng không bao giờ nên được sử dụng trong mã sản xuất, chỉ được sử dụng trong thử nghiệm. Điều đó có đúng không?


19
Theo mặc định, các xác nhận bị vô hiệu hóa. Bạn phải kích hoạt chúng một cách rõ ràng khi chạy qua -eahoặc tương đương.
jarmod

Câu hỏi liên quan về Kỹ thuật phần mềm : softwareengineering.stackexchange.com/q/137158/187318 (mặc dù nó khá cũ, vì vậy câu trả lời được chấp nhận ở đó vẫn đề xuất một thư viện bên ngoài, không còn cần thiết kể từ khi giới thiệu Objects.requireNonNulltrong Java 8).
Hulk

Tiêu đề câu hỏi này quá rộng, nhưng câu hỏi được nêu trong phần thân hẹp hơn nhiều và mỗi câu trả lời được xử lý hoàn toàn bởi các hàm trợ giúp.
smci

Để rõ ràng, bạn đang hỏi liệu mã máy khách có nên thực hiện kiểm tra / xác nhận không null trên các đối tượng hay không. (Điều đó khác với việc hỏi liệu chính mã thư viện có đảm bảo các đối tượng không thể rỗng hay không, hoặc khẳng định rằng có).
smci

Câu trả lời:


19

Sử dụng Objects.requireNonNull(Object)cho điều đó.

Kiểm tra tham chiếu đối tượng đã chỉ định không phải là null. Phương pháp này được thiết kế chủ yếu để thực hiện xác thực tham số trong các phương thức và hàm tạo, [...]

Trong trường hợp của bạn, điều này sẽ là:

public void useObject(CustomObject customObject) {
    object = customObject.getObject();
    Objects.requireNonNull(object);
    // Do stuff using object, which would throw a NPE if object is null.
}

Hàm này được tạo cho các mục đích bạn đã đề cập, tức là đánh dấu rõ ràng những gì không phải là null; cũng trong sản xuất. Lợi ích lớn là bạn đảm bảo rằng bạn tìm thấy giá trị null ngay tại nơi chúng không nên xảy ra ở nơi đầu tiên. Bạn sẽ ít gặp khó khăn hơn trong việc gỡ lỗi các vấn đề gây ra bởi các giá trị null được chuyển đến một nơi nào đó mà chúng không nên.

Một lợi ích khác là tính linh hoạt bổ sung liên quan đến kiểm tra null trái ngược với assert. Mặc dù assertlà một từ khóa để kiểm tra giá trị boolean, Objects.requireNonNull(Object)là một hàm và do đó có thể được nhúng trong mã linh hoạt và dễ đọc hơn nhiều. Ví dụ:

Foo foo = Objects.requireNonNull(service.fetchFoo());

// You cannot write it in on line.
Bar bar = service.fetchBar();
assert bar != null;
service.foo(Objects.requireNonNull(service.getBar()));

// You cannot write it in on line.
Bar bar = service.getBar();
assert bar != null;
service.foo(bar);

Hãy nhớ rằng Objects.requireNonNull(Object)chỉ dành riêng cho việc kiểm tra null, nơi assertđược khái quát hóa. assertcó mục đích hơi khác nhau trong vấn đề đó, tức là chủ yếu thử nghiệm. Nó phải được kích hoạt, vì vậy bạn có thể kích hoạt nó để thử nghiệm và vô hiệu hóa nó để sản xuất. Dù sao, không sử dụng nó cho mã sản xuất. Nó có thể làm chậm ứng dụng với các xác nhận không cần thiết và phức tạp dành cho thử nghiệm. Sử dụng nó để tách riêng kiểm tra chỉ kiểm tra từ các kiểm tra có nghĩa là cho sản xuất là tốt.

Kiểm tra các tài liệu chính thức để biết chi tiết về assert.


14
Ai và khi tuyên bố khẳng định là di sản? Bất kỳ liên kết / tài liệu tham khảo? Tôi không thể tưởng tượng bất kỳ nhà phát triển lành mạnh nào xác định "di sản" công cụ cho phép xác thực dấu chân không có thể dễ dàng kích hoạt trong các thử nghiệm và bị vô hiệu hóa trong sản xuất.
Dmitry Pisklov

Cho rằng bạn có thể quyết định trong thời gian chạy xem assert có hoạt động không, nhưng bạn không thể xác định khi chạy liệu NPE có bị ném bởi requestNonNull hay không, ý của bạn là 'bạn có quyền kiểm soát với nó nhiều hơn so với khẳng định'?
Pete Kirkham

@DmitryPisklov Bạn nói đúng, tôi đã chỉnh sửa nó. Nó là một công cụ tuyệt vời để thử nghiệm.
akuzminykh

2
@PeteKirkham Bạn nói đúng, kiểm soát là từ sai cho điều đó. Tôi đã nói nhiều hơn về sự linh hoạt trong cú pháp. Ngoài ra: đây có thể là lý do tìm thấy lý do tại sao bạn muốn mã ném NPE.
akuzminykh

1
"không sử dụng nó cho mã sản xuất. Nó có thể làm chậm ứng dụng" Nó có thể, nhưng nó có thể có giá trị. Java có các kiểm tra thời gian chạy tích hợp để đảm bảo bạn không bao giờ vượt quá một mảng. Chúng được kích hoạt ngay cả khi triển khai sản xuất, mặc dù có thể bị phạt hiệu suất. Chúng tôi thậm chí không đưa ra một lựa chọn về nó. Không phải lúc nào cũng có kiểm tra thời gian chạy trong mã sản xuất.
Max Barraclough

22

Điều quan trọng nhất cần nhớ về các xác nhận là chúng có thể bị vô hiệu hóa, vì vậy đừng bao giờ cho rằng chúng sẽ bị xử tử.

Để tương thích ngược, JVM vô hiệu hóa xác nhận xác nhận theo mặc định. Chúng phải được kích hoạt một cách rõ ràng bằng cách sử dụng đối số dòng lệnh -enable assertions hoặc tốc ký của nó -ea:

java -ea com.whatever.assertion.Assertion

Vì vậy, nó không phải là một thực hành tốt để dựa vào họ.

Vì các xác nhận không được bật theo mặc định, bạn không bao giờ có thể cho rằng chúng sẽ được thực thi khi được sử dụng trong mã. Vì vậy, bạn phải luôn kiểm tra các giá trị null và Tùy chọn trống, tránh sử dụng các xác nhận để kiểm tra đầu vào thành phương thức công khai và thay vào đó sử dụng một ngoại lệ không được kiểm tra ... Nói chung, hãy thực hiện tất cả các kiểm tra như thể xác nhận đó không có.


Rõ ràng là thực hành xấu để dựa vào họ, nhưng nó là thực hành xấu để sử dụng nó nói chung?
Big_Bad_E

3
@Big_Bad_E Assertions là một công cụ gỡ lỗi tồn tại để làm cho chương trình bị lỗi càng sớm và càng ồn càng tốt. Sau đó, đây là công việc của bộ kiểm tra để đảm bảo rằng không có cách nào một chương trình có thể thất bại mặc dù các xác nhận . Ý tưởng là, một khi bộ kiểm tra (nó có phạm vi bảo hiểm 100%, không phải là?!?) Thực thi mà không gặp sự cố, sẽ tiết kiệm để xóa các xác nhận vì chúng không thể kích hoạt bất kỳ cách nào. Chắc chắn có một chút lý tưởng trong lý do này, nhưng đó là ý tưởng.
cmaster - phục hồi monica

11

Chắc chắn những gì bạn nói là một lời nói dối trắng trợn. Đây là lý do tại sao.

Các xác nhận bị tắt theo mặc định nếu bạn chỉ thực hiện jvm độc lập. Khi chúng bị vô hiệu hóa, chúng không có dấu chân, do đó chúng sẽ không ảnh hưởng đến ứng dụng sản xuất của bạn. Tuy nhiên, họ có thể là những người bạn tốt nhất của bạn khi phát triển và kiểm tra mã của bạn và hầu hết những người chạy khung kiểm tra đều kích hoạt các xác nhận (JUnit), vì vậy mã xác nhận của bạn được thực thi khi bạn chạy thử nghiệm đơn vị của mình, giúp bạn phát hiện bất kỳ lỗi tiềm ẩn nào trước đó (ví dụ: bạn có thể thêm các xác nhận cho một số kiểm tra ranh giới logic nghiệp vụ và điều đó sẽ giúp phát hiện một số mã sử dụng các giá trị không phù hợp).

Điều đó nói rằng, như câu trả lời khác cho thấy, vì chính xác lý do đó (chúng không phải lúc nào cũng được bật), bạn không thể dựa vào các xác nhận để thực hiện một số kiểm tra quan trọng hoặc (đặc biệt là!) Duy trì bất kỳ trạng thái nào.

Để biết một ví dụ thú vị về cách bạn có thể sử dụng các xác nhận, hãy xem ở đây - ở cuối tệp có một phương thức singleThreadedAccess()được gọi từ tuyên bố khẳng định trên dòng 201 và có để bắt bất kỳ quyền truy cập đa luồng tiềm năng nào trong các thử nghiệm.


4

Các câu trả lời khác đã bao gồm điều này đủ tốt, nhưng có những lựa chọn khác.

Ví dụ, Spring có một phương thức tĩnh:

org.springframework.util.Assert.notNull(obj)

Có những thư viện khác với Assert.something()phương pháp riêng của họ là tốt. Nó cũng khá đơn giản để viết của riêng bạn.

Tuy nhiên, hãy ghi nhớ những trường hợp ngoại lệ bạn ném nếu đây là một dịch vụ web. Phương thức trước đó được đề cập, ví dụ, ném một IllegalArgumentExceptioncái theo mặc định trong Spring trả về 500.

Trong trường hợp dịch vụ web, đây thường không phải là lỗi máy chủ nội bộ và không phải là 500, mà là 400, đây là một yêu cầu tồi.


1
Nếu phương pháp "Khẳng định" không ngay lập tức phá vỡ quy trình, thay vào đó, ném một loại ngoại lệ nào đó, thì đó không phải là một khẳng định. Toàn bộ vấn đề assertlà, để có được một sự cố ngay lập tức với một tệp cốt lõi được tạo ra để bạn có thể thực hiện một công việc hậu kỳ với trình gỡ lỗi yêu thích của bạn ngay tại nơi điều kiện bị vi phạm.
cmaster - phục hồi monica

Khẳng định không ngay lập tức sụp đổ một quá trình. Họ ném một Lỗi, có thể bị bắt. Các trường hợp ngoại lệ chưa được xử lý sẽ làm hỏng các bài kiểm tra của bạn cũng như các lỗi. Bạn có thể tranh luận ngữ nghĩa nếu bạn muốn. Nếu bạn rất mong muốn, hãy viết một xác nhận tùy chỉnh ném Lỗi thay vì ngoại lệ. Thực tế là, trong phần lớn các trường hợp, hành động thích hợp là ném một ngoại lệ chứ không phải là một lỗi.
Christopher Schneider

Ah, Java thực sự định nghĩa assertđể ném. Hấp dẫn. Phiên bản C / C ++ không làm như vậy. Nó ngay lập tức phát ra tín hiệu rằng a) giết chết quá trình và b) tạo ra một bãi chứa lõi. Nó thực hiện điều này vì một lý do: Thật đơn giản để gỡ lỗi một lỗi xác nhận như vậy bởi vì bạn vẫn có toàn bộ thông tin về ngăn xếp cuộc gọi khả dụng. assertThay vào đó, xác định để ném một ngoại lệ, sau đó có thể bị bắt (un) cố ý lập trình, đánh bại mục đích, imho.
cmaster - phục hồi monica

Cũng như có một số (hoặc nhiều) điều kỳ lạ trong C và C ++, có một số điều trong Java. Một trong số đó là Throwable. Họ luôn có thể bị bắt. Cho dù đó có phải là điều tốt hay không, tôi không biết. Tôi đoán nó phụ thuộc. Nhiều chương trình Java là các dịch vụ web và sẽ không mong muốn nó bị sập vì hầu hết mọi lý do, vì vậy mọi thứ đều bị bắt và ghi lại. Một trong những điều tuyệt vời là dấu vết ngăn xếp và thường đủ để chẩn đoán lý do cho một ngoại lệ hoặc lỗi.
Christopher Schneider

3

Sử dụng khẳng định một cách tự do bất cứ khi nào làm như vậy giúp nắm bắt các lỗi lập trình tức là lỗi.

Không sử dụng khẳng định để bắt một cái gì đó có thể xảy ra một cách hợp lý tức là đầu vào được định dạng sai. Chỉ sử dụng xác nhận khi lỗi không thể phục hồi.

Không đặt bất kỳ logic sản xuất nào vào mã chạy khi xác nhận được kiểm tra. Nếu phần mềm của bạn được viết tốt thì điều này là đúng nhưng nếu không thì bạn có thể có các tác dụng phụ tinh tế và hành vi tổng thể khác nhau với các xác nhận được bật và tắt.

Nếu công ty của bạn có "mã thử nghiệm" và "mã sản xuất" làm điều tương tự nhưng là các cơ sở mã khác nhau (hoặc các giai đoạn chỉnh sửa khác nhau), hãy ra khỏi đó và không bao giờ quay lại. Cố gắng khắc phục mức độ bất tài đó có lẽ là một sự lãng phí thời gian của bạn. Nếu công ty của bạn không đặt bất kỳ tuyên bố khẳng định nào bên ngoài mã của các bài kiểm tra, vui lòng cho họ biết rằng các xác nhận bị vô hiệu hóa trong bản dựng sản xuất và nếu không, sửa lỗi đó là ưu tiên hàng đầu của bạn.

Giá trị của các xác nhận chính xác được sử dụng bên trong logic nghiệp vụ và không chỉ bộ thử nghiệm. Điều này giúp dễ dàng đưa ra nhiều bài kiểm tra cấp cao mà không phải kiểm tra rõ ràng nhiều thứ để vượt qua các đoạn mã lớn của bạn và kích hoạt tất cả các xác nhận này. Trong một vài dự án thử nghiệm điển hình của tôi thậm chí không thực sự khẳng định bất cứ điều gì, họ chỉ yêu cầu một phép tính xảy ra dựa trên đầu vào cụ thể và điều này khiến hàng trăm xác nhận được kiểm tra và các vấn đề được tìm thấy ngay cả trong những mảnh logic nhỏ.


2

Bạn có thể sử dụng khẳng định bất cứ lúc nào. Các cuộc tranh luận là khi sử dụng. Ví dụ trong hướng dẫn :

  • Không sử dụng các xác nhận để kiểm tra đối số trong các phương thức công khai.
  • Không sử dụng các xác nhận để thực hiện bất kỳ công việc nào mà ứng dụng của bạn yêu cầu cho hoạt động chính xác.

4
Quy tắc tốt. Nhưng câu trả lời sẽ tốt hơn với một số lý do được thêm vào.
cmaster - phục hồi monica
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.