关于嵌套Java尝试/最终代码三明治的建议
我想要一些关于我碰到的技术的建议。通过查看代码片段可以很容易地理解它,但我在下面的段落中记录了它。
使用“代码三明治”成语来处理资源管理是司空见惯的。习惯了C++的 RAII 习语,我切换到 Java,发现我的异常安全资源管理导致了深度嵌套的代码,在这种代码中,我很难掌握常规的控制流。
显然(java数据访问:这是Java数据访问代码的好风格,还是最后尝试太多了?,Java io丑陋的尝试-最终阻止等等)我并不孤单。
我尝试了不同的解决方案来解决这个问题:
显式维护程序状态:、...,并有条件地清理:...但是我避免在显式变量中复制程序状态 - 运行时知道状态,我不想关心它。
resource1aquired
fileopened
if (resource1acquired) resource1.cleanup()
将每个嵌套块包装在函数中 - 导致更难遵循控制流,并使函数名称非常尴尬:,,...
runResource1Acquired( r1 )
runFileOpened( r1, file )
最后,我得出了一个成语,这个成语也(概念上)得到了一些关于代码三明治的研究论文的支持:
取而代之的是:
// (pseudocode)
try {
connection = DBusConnection.SessionBus(); // may throw, needs cleanup
try {
exported = false;
connection.export("/MyObject", myObject ); // may throw, needs cleanup
exported = true;
//... more try{}finally{} nested blocks
} finally {
if( exported ) connection.unExport( "/MyObject" );
}
} finally {
if (connection != null ) connection.disconnect();
}
使用帮助器构造,您可以得到一个更加线性的构造,其中补偿代码紧挨着发起方。
class Compensation {
public void compensate(){};
}
compensations = new Stack<Compensation>();
嵌套代码变为线性:
try {
connection = DBusConnection.SessionBus(); // may throw, needs cleanup
compensations.push( new Compensation(){ public void compensate() {
connection.disconnect();
});
connection.export("/MyObject", myObject ); // may throw, needs cleanup
compensations.push( new Compensation(){ public void compensate() {
connection.unExport( "/MyObject" );
});
// unfolded try{}finally{} code
} finally {
while( !compensations.empty() )
compensations.pop().compensate();
}
我很高兴:无论有多少例外路径,控制流都保持线性,清理代码在视觉上紧挨着原始代码。最重要的是,它不需要人为限制的方法,这使得它更加灵活(即不仅是对象,而且是,以及其他任何方法)。closeQuietly
Closeable
Disconnectable
Rollbackable
但。。。
我没有在其他地方发现提到这种技术。所以问题是:
此技术是否有效?你在其中看到了什么错误?
多谢。