1. private void openACMEPort() {
  2. ACMEPort port = new ACMEPort(12);
  3. try {
  4. port.open();
  5. } catch (DeviceResponseException e) {
  6. reportPortError(e);
  7. logger.log("Device response exception", e);
  8. } catch (ATM1212UnlockedException e) {
  9. reportPortError(e);
  10. logger.log("Unlock exception", e);
  11. } catch (GMXError e) {
  12. reportPortError(e);
  13. logger.log("Device response exception");
  14. } finally {
  15. }
  16. }

上面包含了一大堆重复代码,这并不出奇。在大多数异常处理中,不管真实原因如何,我们总是做相对标准的处理。我们得记录错误,确保能继续工作。

在本例中,既然知道我们所做的事不外如此,就可以通过打包调用 API、确保它返回通用异常类型,从而简化代码。

  1. private void openACMEPort() {
  2. LocalPort port = new LocalPort(12);
  3. try {
  4. port.open();
  5. } catch (PortDeviceFailure e) {
  6. reportError(e);
  7. logger.log(e.getMessage(), e);
  8. } finally {…}
  9. }
  1. public class LocalPort {
  2. private ACMEPort innerPort;
  3. public LocalPort(int portNumber) {
  4. innerPort = new ACMEPort(portNumber);
  5. }
  6. public void open() {
  7. try {
  8. innerPort.open();
  9. } catch (DeviceResponseException e) {
  10. throw new PortDeviceFailure(e);
  11. } catch (ATM1212UnlockedException e) {
  12. throw new PortDeviceFailure(e);
  13. } catch (GMXError e) {
  14. throw new PortDeviceFailure(e);
  15. }
  16. }
  17. }

类似我们为ACMEPort定义的这种打包类非常有用。实际上,将第三方API打包是个良好的实践手段。当你打包一个第三方 API,你就降低了对它的依赖:未来你可以不太痛苦地改用其他代码库。在你测试自己的代码时,打包也有助于模拟第三方调用。

别返回null值

  1. public void registerItem(Item item) {
  2. if (item != null) {
  3. ItemRegistry registry = peristentStore.getItemRegistry();
  4. if (registry != null) {
  5. Item existing = registry.getItem(item.getID());
  6. if (existing.getBillingPeriod().hasRetailOwner()) {
  7. existing.register(item);
  8. }
  9. }
  10. }
  11. }

这种代码看似不坏,其实糟透了!返回null值,基本上是在给自己增加工作量,也是在给调用者添乱。只要有一处没检查null值,应用程序就会失控。

可以敷衍说上列代码的问题是少做了一次null值检查,其实问题多多。如果你打算在方法中返回null值,不如抛出异常,或是返回特例对象。如果你在调用某个第三方API中可能返回null值的方法,可以考虑用新方法打包这个方法,在新方法中抛出异常或返回特例对象。

在许多情况下,特例对象都是爽口良药。设想有这么一段代码:

  1. List<Employee> employees = getEmployees();
  2. if (employees != null) {
  3. for (Employee e : employees) {
  4. totalPay += e.getPay();
  5. }
  6. }

现在,getExployees可能返回null,但是否一定要这么做呢?如果修改getEmployee,返回空列表,就能使代码整洁起来:

  1. List<Employee> employees = getEmployees();
  2. for(Employee e : employees) {
  3. totalPay += e.getPay();
  4. }

别传递null值

在方法中返回null值是糟糕的做法,但将null值传递给其他方法就更糟糕了。除非API要求你向它传递null值,否则就要尽可能避免传递null值。

  1. public class MetricsCalculator {
  2. public double xProjection(Point p1, Point p2) {
  3. return (p2.x p1.x) *1.5;
  4. }
  5. }


如果有人传入null值会怎样?

calculator.xProjection(null, new Point(12, 13));
当然,我们会得到一个NullPointerException异常。

  1. public class MetricsCalculator {
  2. public double xProjection(Point p1, Point p2) {
  3. if (p1 == null || p2 == null) {
  4. throw InvalidArgumentException("Invalid argument for MetricsCalculator.xProjection");
  5. }
  6. return (p2.x p1.x) *1.5;
  7. }
  8. }

可能比null指针异常好一些,但要记住,我们还得为InvalidArgumentException异常定义处理器。这个处理器该做什么?还有更好的做法吗?

还有替代方案。可以使用一组断言:

  1. public class MetricsCalculator {
  2. public double xProjection(Point p1, Point p2) {
  3. assert p1 != null : "p1 should not be null";
  4. assert p2 != null : "p2 should not be null";
  5. return (p2.x p1.x) *1.5;
  6. }
  7. }

看上去很美,但仍未解决问题。如果有人传入null值,还是会得到运行时错误。
在大多数编程语言中,没有良好的方法能对付由调用者意外传入的null值。事已如此,恰当的做法就是禁止传入null值。