我想就我碰到的技術提出一些建議。通過查看代碼片段可以很容易理解,但我在下面的段落中進一步記錄了它。關於嵌套Java嘗試/最後代碼三明治的建議
使用「代碼三明治」成語在處理資源管理中是司空見慣的。習慣了C++的RAII習慣用法,我切換到了Java,發現我的異常安全的資源管理導致了深度嵌套的代碼,在這個代碼中,我很難掌握控制流程。
顯然(java data access: is this good style of java data access code, or is it too much try finally?,Java io ugly try-finally block和更多)我並不孤單。
我嘗試了不同的解決方案來應對這樣的:
明確維護程序狀態:
resource1aquired
,fileopened
...,和清理條件:if (resource1acquired) resource1.cleanup()
......但我在順明確複製程序狀態變量 - 運行時知道狀態,我不想關心它。包裹在每一個功能嵌套塊 - 結果更難遵循控制流程,併爲真正尷尬的函數名稱:
runResource1Acquired(r1)
,runFileOpened(r1, file)
,...
最後我來到了一個成語
取而代之的是:
也(概念)的一些 research paper on code sandwiches支持0使用助手構造,您可能會得到一個更線性的構造,其中補償代碼緊挨着始發者。
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
以及其他任何其他)。
但是...
我在其他地方沒有發現這種技術。所以這裏的問題是:
該技術是否有效?你看到了什麼錯誤?
非常感謝。
你似乎沒有被處理的情況下一個可以從一個compensation()方法中拋出異常。這將阻止後續的補償運行。 –
@Kevin:確實 - 這裏有太多的C++習慣用法:析構函數不能拋出,我堅持這個習慣用法。 「補償」實現可以處理異常。 – xtofl
@xtofl - 與你使用'try-finally'完全不一樣 - 你可以在代碼本身採用這個習慣用法,而不用爲'finally'塊打擾。此外,許多'Compensators'自然會拋出異常(例如,用於關閉數據庫資源的'SQLException');每個實現**都有**來單獨捕獲和吞下所有異常,而不是你的'finally'塊在一個地方一致地處理它。這對他們來說不是可選的(運行時異常會違反他們的規範,並且可能隨時發生),所以您只需鼓勵複製粘貼。 –