Khi tôi đang cố gắng tạo giao diện cho một chương trình cụ thể, tôi thường cố gắng tránh đưa ra các ngoại lệ phụ thuộc vào đầu vào không được xác thực.
Vì vậy, điều thường xảy ra là tôi đã nghĩ về một đoạn mã như thế này (đây chỉ là một ví dụ vì lợi ích của một ví dụ, đừng bận tâm đến chức năng mà nó thực hiện, ví dụ trong Java):
public static String padToEvenOriginal(int evenSize, String string) {
if (evenSize % 2 == 1) {
throw new IllegalArgumentException("evenSize argument is not even");
}
if (string.length() >= evenSize) {
return string;
}
StringBuilder sb = new StringBuilder(evenSize);
sb.append(string);
for (int i = string.length(); i < evenSize; i++) {
sb.append(' ');
}
return sb.toString();
}
OK, vì vậy nói rằng evenSize
thực sự có nguồn gốc từ đầu vào của người dùng. Vì vậy, tôi không chắc chắn rằng nó là thậm chí. Nhưng tôi không muốn gọi phương thức này với khả năng một ngoại lệ được đưa ra. Vì vậy, tôi thực hiện các chức năng sau đây:
public static boolean isEven(int evenSize) {
return evenSize % 2 == 0;
}
nhưng bây giờ tôi đã có hai kiểm tra thực hiện xác nhận đầu vào giống nhau: biểu thức trong if
câu lệnh và kiểm tra rõ ràng isEven
. Mã trùng lặp, không đẹp, vì vậy hãy tái cấu trúc:
public static String padToEvenWithIsEven(int evenSize, String string) {
if (!isEven(evenSize)) { // to avoid duplicate code
throw new IllegalArgumentException("evenSize argument is not even");
}
if (string.length() >= evenSize) {
return string;
}
StringBuilder sb = new StringBuilder(evenSize);
sb.append(string);
for (int i = string.length(); i < evenSize; i++) {
sb.append(' ');
}
return sb.toString();
}
OK, điều đó đã giải quyết nó, nhưng bây giờ chúng ta gặp phải tình huống sau:
String test = "123";
int size;
do {
size = getSizeFromInput();
} while (!isEven(size)); // checks if it is even
String evenTest = padToEvenWithIsEven(size, test);
System.out.println(evenTest); // checks if it is even (redundant)
bây giờ chúng tôi đã có một kiểm tra dự phòng: chúng tôi đã biết rằng giá trị là chẵn, nhưng padToEvenWithIsEven
vẫn thực hiện kiểm tra tham số, sẽ luôn trả về giá trị đúng, như chúng tôi đã gọi hàm này.
Bây giờ isEven
tất nhiên không gây ra vấn đề gì, nhưng nếu việc kiểm tra tham số trở nên cồng kềnh hơn thì điều này có thể phải chịu quá nhiều chi phí. Bên cạnh đó, thực hiện một cuộc gọi dự phòng đơn giản là không cảm thấy đúng.
Đôi khi chúng ta có thể giải quyết vấn đề này bằng cách giới thiệu "loại được xác thực" hoặc bằng cách tạo một hàm trong đó vấn đề này không thể xảy ra:
public static String padToEvenSmarter(int numberOfBigrams, String string) {
int size = numberOfBigrams * 2;
if (string.length() >= size) {
return string;
}
StringBuilder sb = new StringBuilder(size);
sb.append(string);
for (int i = string.length(); i < size; i++) {
sb.append('x');
}
return sb.toString();
}
nhưng điều này đòi hỏi một số suy nghĩ thông minh và một công cụ tái cấu trúc khá lớn.
Có một cách chung (hơn) trong đó chúng ta có thể tránh các cuộc gọi dự phòng isEven
và thực hiện kiểm tra tham số kép không? Tôi muốn giải pháp không thực sự gọi padToEven
với một tham số không hợp lệ, gây ra ngoại lệ.
Không có ngoại lệ, tôi không có nghĩa là lập trình không có ngoại lệ, ý tôi là đầu vào của người dùng không kích hoạt ngoại lệ theo thiết kế, trong khi bản thân hàm chung vẫn chứa kiểm tra tham số (nếu chỉ để bảo vệ chống lại lỗi lập trình).
padToEvenWithIsEven
không thực hiện xác nhận đầu vào của người dùng. Nó thực hiện kiểm tra tính hợp lệ trên đầu vào của nó để bảo vệ chính nó khỏi các lỗi lập trình trong mã gọi. Việc xác thực này cần bao nhiêu tùy thuộc vào phân tích chi phí / rủi ro khi bạn đặt chi phí kiểm tra đối với rủi ro mà người viết mã gọi vượt qua trong tham số sai.