Vorrei qualche consiglio su una tecnica su cui mi sono imbattuto. Può essere facilmente compreso guardando i frammenti di codice, ma lo documenterò un po 'di più nei paragrafi seguenti.consigli su nested Java try/finally code sandwiches
L'utilizzo dell'idioma "Code Sandwich" è comune per gestire la gestione delle risorse. Utilizzato per l'idioma RAII di C++, sono passato a Java e ho trovato la mia gestione delle risorse eccezionalmente sicura con conseguente codice profondamente annidato, in cui ho davvero un momento difficile ottenere grip sul flusso di controllo regolare .
Apparentemente (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 e molti altri) Non sono solo.
ho provato diverse soluzioni per far fronte a questo:
mantenere lo stato del programma in modo esplicito:
resource1aquired
,fileopened
..., e la pulizia condizionatamente:if (resource1acquired) resource1.cleanup()
... Ma mi evitano la duplicazione dello stato del programma in esplicita variabili - il runtime conosce lo stato e non voglio preoccuparmene.avvolgere ogni blocco nidificato nelle funzioni - si traduce in ancora più difficile da seguire il flusso di controllo, e rende per i nomi delle funzioni davvero imbarazzante:
runResource1Acquired(r1)
,runFileOpened(r1, file)
, ...
E finalmente sono arrivato a un idioma anche (concettualmente) sostenuta da alcuni research paper on code sandwiches:
Invece di questo:
// (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();
}
Utilizzando una costruzione di supporto, è possibile arrivare a un costrutto più lineare, in cui il codice di compensazione si trova proprio accanto all'originatore.
class Compensation {
public void compensate(){};
}
compensations = new Stack<Compensation>();
E il codice nidificato diventa lineare:
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();
}
mi ha fatto molto piacere: non importa quante percorsi eccezionali, il flusso di controllo rimane lineare, e il codice di pulitura è visivamente accanto al codice di origine. Inoltre, non è necessario un metodo closeQuietly
limitato artificialmente, che lo rende più flessibile (ad esempio non solo gli oggetti Closeable
, ma anche Disconnectable
, Rollbackable
e qualsiasi altro).
Ma ...
ho trovato alcuna menzione di questa tecnica altrove. Quindi, ecco la domanda:
Questa tecnica è valida? Quali bug vedi?
Grazie mille.
Non sembrano essere si occupano del caso in cui un'eccezione potrebbe essere generata da un metodo compensate(). Ciò impedirebbe il susseguirsi di compensazioni successive. –
@Kevin: in effetti - troppa idioma C++ qui: i distruttori non devono gettare, e io mi attengo a quell'idioma qui. Dipende dalle implementazioni di "compensare" per gestire le eccezioni. – xtofl
@xtofl - che sembra in disaccordo con te usando 'try-finally' - puoi semplicemente adottare quell'idioma con il codice stesso, e non preoccuparti di un blocco' finally'. Inoltre, molti 'Compensator' genererebbero naturalmente delle eccezioni (ad esempio' SQLException' per chiudere una risorsa DB); ogni implementazione ** ha ** di catturare e ingoiare tutte le eccezioni singolarmente, piuttosto che il blocco 'finally' che lo gestisce in modo coerente in un unico posto. Non è facoltativo per loro farlo (un'eccezione di runtime potrebbe violare le loro specifiche, e ciò potrebbe accadere in qualsiasi momento), quindi sarebbe solo incoraggiante copia-incolla. –