] совет по вложенным бутербродам с кодом «попробуйте / наконец-то» Java [

] [

] Я хотел бы получить совет по технике, на которую я наткнулся. Это можно легко понять, просмотрев фрагменты кода, но я документирую это несколько подробнее в следующих абзацах. [

] [


] [

] Использование идиомы «Code Sandwich» - обычное дело для управления ресурсами. Привыкнув к идиоме RAII в C ++, я переключился на Java и обнаружил, что мое безопасное в отношении исключений управление ресурсами приводит к глубоко вложенному коду, в котором мне действительно трудно справиться с [] обычным [] потоком управления. [

] [

] Очевидно ([] доступ к данным java: это хороший стиль кода доступа к данным java, или это слишком много попыток, наконец? [], [] Уродливый блок попыток Java io [] и многое другое ) Я не один. [

] [

] Я пробовал разные решения, чтобы справиться с этим: [

] [

    ] [
  1. ] [

    ] явно поддерживает состояние программы: [] resource1aquired [], [] fileopened [] ... и очистка условно: [] if (resource1acquired) resource1.cleanup () [] ... Но я избегаю дублирования состояния программы в явных переменных - среда выполнения знает состояние, и я не хочу о нем заботиться.[

    ] [
  2. ] [
  3. ] [

    ] обернуть каждый вложенный блок в функции - еще больше усложняет отслеживание потока управления и делает имена функций действительно неудобными: [] runResource1Acquired (r1) [], [] runFileOpened (r1, file) [], ... [

    ] [
  4. ] [

] [

] И, наконец, я пришел к идиоме (концептуально), подкрепленной некоторой [] исследовательской статьей о сэндвичах с кодом []: [

] [


] [

] Вместо этого: [

] [

// (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 [] и любые другие). [

] [

] Но ... [

] [

] Я не нашел упоминания об этой технике в другом месте. Итак, вот вопрос: [

] [


] [

] Действует ли этот метод? Какие ошибки вы в нем видите? [

] [

] Большое спасибо. [

]

21
задан Community 23 May 2017 в 12:34
поделиться