] [
] Я хотел бы получить совет по технике, на которую я наткнулся. Это можно легко понять, просмотрев фрагменты кода, но я документирую это несколько подробнее в следующих абзацах. [
] [
] [
] Использование идиомы «Code Sandwich» - обычное дело для управления ресурсами. Привыкнув к идиоме RAII в C ++, я переключился на 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();
] [
] И вложенный код становится линейным: [
] [
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 [
] и любые другие). [
] [
] Но ... [
] [
] Я не нашел упоминания об этой технике в другом месте. Итак, вот вопрос: [
] [
] [
] Действует ли этот метод? Какие ошибки вы в нем видите? [
] [
] Большое спасибо. [
]