在当前的Java项目中,我们有类似于以下示例的代码:
try { doSomeThing(anObject); } catch (SameException e) { // Do nothing or log, but don't abort current method. } try { doOtherThing(anObject); } catch (SameException e) { // Do nothing or log, but don't abort current method. } // ... some more calls to different method ... try { finallyDoYetSomethingCompletelyDifferent(anObject); } catch (SameException e) { // Do nothing or log, but don't abort current method. }
正如您所看到的,使用完全相同的对象调用了几个不同的方法,并且对于每个调用,捕获并处理相同的异常(或以非常类似的方式).异常不会被重新抛出,但可能只会被记录然后被丢弃.
try-catch
围绕每个方法存在的唯一原因是始终执行所有方法,无论先前执行的方法是否失败.
我根本不喜欢上面的代码.它占用了大量的空间,非常重复(尤其是在catch
-block中完成的日志记录;此处未提供)并且看起来很糟糕.
我可以想到其他一些编写代码的方法,但是也不太喜欢它们.我想到了以下选项:
循环切换顺序/用于案例范例
(参见维基百科或每日WTF)
for (int i = 0; i <= 9; i++) { try { switch (i) { case 0: doSomeThing(anObject); break; case 1: doOtherSomeThing(anObject); break; // ...More cases... case 9: doYetSomethingCompletelyDifferent(anObject); break; } } catch (SameException e) { // Do nothing or log, but don't abort current method. } }
这显然是错误的代码,非常容易出错并且看起来很业余.
反射
使用反射来获取Method
方法的对象,以便按照它们应该执行的顺序将它们存储在列表中.然后迭代此列表并使用anObject
as only参数调用该方法.异常在循环内部处理.
我不喜欢这种方法,因为错误(例如方法名称中的拼写错误)仅在运行时弹出,而Reflection API有些繁琐.
函子
像这样创建一个Functor类:
private class Functor { void doStuff(MyObject object) throws SameException; }
然后创建一个Functor
调用方法的对象列表.像这样:
Listfunctors = new ArrayList (); functors.add(new Functor() { @Override public void execute(MyObject anObject) { doSomeThing(anObject); } }); functors.add(new Functor() { @Override public void execute(MyObject anObject) { doOtherSomeThing(anObject); } });
稍后,迭代此列表并调用execute()
每个Functor
对象.我可以用两个词来总结我对这种方法的感觉:代码膨胀.
由于我不喜欢这四种方法,我想在这里讨论这个问题.你觉得最好的方法是什么?你是怎么解决过去的类似问题的?是否有一个我完全错过的更简单的解决方案?
我会提倡重构方法(或" 我们为什么一开始就到这里来? "):
考虑为什么单个方法可以在使用myObject执行"stuff"之后抛出异常,然后可以安全地忽略该异常.由于异常会转义该方法,因此myObject必须处于未知状态.
如果忽略异常是安全的,那么肯定是错误的方式来传达每个方法中出错的地方.
相反,也许每个方法都需要对失败进行一些记录.如果不使用静态记录器,则可以将记录器传递给每个方法.
仿函数方法是我心目中最好的方法 - 遗憾的是,Java没有更好的方式来表示闭包或代理.这基本上就是你真正想要的,而在C#(和许多其他语言)中,这将是微不足道的.
你可以通过以下方式减少物理膨胀:
Functor[] functors = new Functor[] { new Functor() { @Override public void execute(MyObject anObject) { doSomeThing(anObject); }}, new Functor() { @Override public void execute(MyObject anObject) { doSomeOtherThing(anObject); }} };
在这里崩溃的空白可能会违背你正在使用的风格指南,但我认为它使代码更容易实际阅读,因为你可以更容易地看到肉.
最好开始游说Java 8中的闭包;)