当我尝试为特定程序创建接口时,通常是在尝试避免引发依赖于未经验证的输入的异常。
因此,经常发生的事情是我想到了这样的一段代码(出于示例目的,这只是一个示例,不要介意它执行的功能,例如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();
}
好的,可以说这evenSize
实际上是从用户输入中得出的。所以我不确定它是否是偶数。但是我不想在抛出异常的情况下调用此方法。因此,我执行以下功能:
public static boolean isEven(int evenSize) {
return evenSize % 2 == 0;
}
但是现在我有两个执行相同输入验证的检查:if
语句中的表达式和中的显式check isEven
。代码重复,不好,所以让我们重构一下:
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();
}
好的,就解决了,但是现在我们进入了以下情况:
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)
现在我们有了一个多余的检查:我们已经知道该值是偶数,但是padToEvenWithIsEven
仍然执行参数检查,因为我们已经调用了此函数,所以它将始终返回true。
现在isEven
当然不会造成问题,但是如果参数检查比较麻烦,则可能会产生过多的成本。除此之外,执行冗余呼叫根本感觉不对。
有时,我们可以通过引入“验证类型”或通过创建一个不会发生此问题的函数来解决此问题:
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();
}
但这需要一些明智的思考和相当大的重构。
有没有一种(更多)通用的方式可以避免重复调用isEven
和执行双参数检查?我希望解决方案不要实际padToEven
使用无效的参数来触发异常。
毫无例外,我并不是说无异常编程,我是指用户输入不会通过设计触发异常,而泛型函数本身仍包含参数检查(如果只是为了防止编程错误)。
padToEvenWithIsEven
它不执行用户输入的验证。它对其输入执行有效性检查,以保护自己免受调用代码中的编程错误的影响。此验证需要进行的广泛程度取决于成本/风险分析,在此过程中,您将检查成本与编写调用代码的人传递错误参数的风险进行对比。