Điều này đối với tôi nghe có vẻ như là một vấn đề khá phổ biến mà các nhà phát triển từ trung cấp đến trung cấp có xu hướng phải đối mặt vào một lúc nào đó: họ không biết hoặc không tin vào các hợp đồng mà họ đang tham gia và phòng thủ quá mức cho null. Ngoài ra, khi viết mã của riêng họ, họ có xu hướng dựa vào trả về null để chỉ ra điều gì đó do đó yêu cầu người gọi kiểm tra null.
Nói cách khác, có hai trường hợp kiểm tra null xuất hiện:
Trong đó null là một phản hồi hợp lệ về mặt hợp đồng; và
Trường hợp đó không phải là một phản hồi hợp lệ.
(2) là dễ dàng. Sử dụng các assert
câu lệnh (xác nhận) hoặc cho phép thất bại (ví dụ: NullPulumException ). Các xác nhận là một tính năng Java được sử dụng nhiều đã được thêm vào trong 1.4. Cú pháp là:
assert <condition>
hoặc là
assert <condition> : <object>
trong đó <condition>
một biểu thức boolean và <object>
là một đối tượng có toString()
đầu ra của phương thức sẽ được bao gồm trong lỗi.
Một assert
câu lệnh ném một Error
( AssertionError
) nếu điều kiện không đúng. Theo mặc định, Java bỏ qua các xác nhận. Bạn có thể kích hoạt các xác nhận bằng cách chuyển tùy chọn -ea
cho JVM. Bạn có thể kích hoạt và vô hiệu hóa các xác nhận cho các lớp và gói riêng lẻ. Điều này có nghĩa là bạn có thể xác thực mã với các xác nhận trong khi phát triển và thử nghiệm và vô hiệu hóa chúng trong môi trường sản xuất, mặc dù thử nghiệm của tôi đã cho thấy bên cạnh không có tác động hiệu suất từ các xác nhận.
Không sử dụng các xác nhận trong trường hợp này là OK vì mã sẽ thất bại, đó là điều sẽ xảy ra nếu bạn sử dụng các xác nhận. Sự khác biệt duy nhất là với các xác nhận nó có thể xảy ra sớm hơn, theo cách có ý nghĩa hơn và có thể có thêm thông tin, điều này có thể giúp bạn tìm ra lý do tại sao nó xảy ra nếu bạn không mong đợi nó.
(1) khó hơn một chút. Nếu bạn không kiểm soát được mã bạn đang gọi thì bạn bị kẹt. Nếu null là một phản hồi hợp lệ, bạn phải kiểm tra nó.
Tuy nhiên, nếu đó là mã mà bạn kiểm soát (và đây thường là trường hợp), thì đó là một câu chuyện khác. Tránh sử dụng null làm phản hồi. Với các phương thức trả về các bộ sưu tập, thật dễ dàng: trả về các bộ sưu tập trống (hoặc mảng) thay vì null khá nhiều lần.
Với các bộ sưu tập không phải là khó hơn. Hãy xem đây là một ví dụ: nếu bạn có các giao diện này:
public interface Action {
void doSomething();
}
public interface Parser {
Action findAction(String userInput);
}
trong đó Parser lấy đầu vào của người dùng thô và tìm thấy việc cần làm, có lẽ nếu bạn đang thực hiện giao diện dòng lệnh cho một cái gì đó. Bây giờ bạn có thể làm cho hợp đồng trở lại vô hiệu nếu không có hành động thích hợp. Điều đó dẫn đến việc kiểm tra null mà bạn đang nói đến.
Một giải pháp thay thế là không bao giờ trả về null và thay vào đó sử dụng mẫu Null Object :
public class MyParser implements Parser {
private static Action DO_NOTHING = new Action() {
public void doSomething() { /* do nothing */ }
};
public Action findAction(String userInput) {
// ...
if ( /* we can't find any actions */ ) {
return DO_NOTHING;
}
}
}
Đối chiếu:
Parser parser = ParserFactory.getParser();
if (parser == null) {
// now what?
// this would be an example of where null isn't (or shouldn't be) a valid response
}
Action action = parser.findAction(someInput);
if (action == null) {
// do nothing
} else {
action.doSomething();
}
đến
ParserFactory.getParser().findAction(someInput).doSomething();
đó là một thiết kế tốt hơn nhiều vì nó dẫn đến mã ngắn gọn hơn.
Điều đó nói rằng, có lẽ nó hoàn toàn phù hợp với phương thức findAction () để ném Ngoại lệ với thông báo lỗi có ý nghĩa - đặc biệt trong trường hợp này bạn đang dựa vào đầu vào của người dùng. Sẽ tốt hơn nhiều nếu phương thức findAction đưa ra một Ngoại lệ so với phương thức gọi để làm nổ tung với một NullPulumException đơn giản mà không có lời giải thích.
try {
ParserFactory.getParser().findAction(someInput).doSomething();
} catch(ActionNotFoundException anfe) {
userConsole.err(anfe.getMessage());
}
Hoặc nếu bạn nghĩ rằng cơ chế thử / bắt quá xấu, thay vì Không làm gì, hành động mặc định của bạn sẽ cung cấp phản hồi cho người dùng.
public Action findAction(final String userInput) {
/* Code to return requested Action if found */
return new Action() {
public void doSomething() {
userConsole.err("Action not found: " + userInput);
}
}
}