private void openACMEPort() {
ACMEPort port = new ACMEPort(12);
try {
port.open();
} catch (DeviceResponseException e) {
reportPortError(e);
logger.log("Device response exception", e);
} catch (ATM1212UnlockedException e) {
reportPortError(e);
logger.log("Unlock exception", e);
} catch (GMXError e) {
reportPortError(e);
logger.log("Device response exception");
} finally {
}
}
上面包含了一大堆重复代码,这并不出奇。在大多数异常处理中,不管真实原因如何,我们总是做相对标准的处理。我们得记录错误,确保能继续工作。
在本例中,既然知道我们所做的事不外如此,就可以通过打包调用 API、确保它返回通用异常类型,从而简化代码。
private void openACMEPort() {
LocalPort port = new LocalPort(12);
try {
port.open();
} catch (PortDeviceFailure e) {
reportError(e);
logger.log(e.getMessage(), e);
} finally {…}
}
public class LocalPort {
private ACMEPort innerPort;
public LocalPort(int portNumber) {
innerPort = new ACMEPort(portNumber);
}
public void open() {
try {
innerPort.open();
} catch (DeviceResponseException e) {
throw new PortDeviceFailure(e);
} catch (ATM1212UnlockedException e) {
throw new PortDeviceFailure(e);
} catch (GMXError e) {
throw new PortDeviceFailure(e);
}
}
}
类似我们为ACMEPort定义的这种打包类非常有用。实际上,将第三方API打包是个良好的实践手段。当你打包一个第三方 API,你就降低了对它的依赖:未来你可以不太痛苦地改用其他代码库。在你测试自己的代码时,打包也有助于模拟第三方调用。
别返回null值
public void registerItem(Item item) {
if (item != null) {
ItemRegistry registry = peristentStore.getItemRegistry();
if (registry != null) {
Item existing = registry.getItem(item.getID());
if (existing.getBillingPeriod().hasRetailOwner()) {
existing.register(item);
}
}
}
}
这种代码看似不坏,其实糟透了!返回null值,基本上是在给自己增加工作量,也是在给调用者添乱。只要有一处没检查null值,应用程序就会失控。
可以敷衍说上列代码的问题是少做了一次null值检查,其实问题多多。如果你打算在方法中返回null值,不如抛出异常,或是返回特例对象。如果你在调用某个第三方API中可能返回null值的方法,可以考虑用新方法打包这个方法,在新方法中抛出异常或返回特例对象。
在许多情况下,特例对象都是爽口良药。设想有这么一段代码:
List<Employee> employees = getEmployees();
if (employees != null) {
for (Employee e : employees) {
totalPay += e.getPay();
}
}
现在,getExployees可能返回null,但是否一定要这么做呢?如果修改getEmployee,返回空列表,就能使代码整洁起来:
List<Employee> employees = getEmployees();
for(Employee e : employees) {
totalPay += e.getPay();
}
别传递null值
在方法中返回null值是糟糕的做法,但将null值传递给其他方法就更糟糕了。除非API要求你向它传递null值,否则就要尽可能避免传递null值。
public class MetricsCalculator {
public double xProjection(Point p1, Point p2) {
return (p2.x – p1.x) *1.5;
}
}
如果有人传入null值会怎样?
calculator.xProjection(null, new Point(12, 13));
当然,我们会得到一个NullPointerException异常。
public class MetricsCalculator {
public double xProjection(Point p1, Point p2) {
if (p1 == null || p2 == null) {
throw InvalidArgumentException("Invalid argument for MetricsCalculator.xProjection");
}
return (p2.x –p1.x) *1.5;
}
}
可能比null指针异常好一些,但要记住,我们还得为InvalidArgumentException异常定义处理器。这个处理器该做什么?还有更好的做法吗?
还有替代方案。可以使用一组断言:
public class MetricsCalculator {
public double xProjection(Point p1, Point p2) {
assert p1 != null : "p1 should not be null";
assert p2 != null : "p2 should not be null";
return (p2.x –p1.x) *1.5;
}
}
看上去很美,但仍未解决问题。如果有人传入null值,还是会得到运行时错误。
在大多数编程语言中,没有良好的方法能对付由调用者意外传入的null值。事已如此,恰当的做法就是禁止传入null值。