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值。
