Ho sempre avuto dubbi quando si tratta di progettare un report di esecuzione corretto.Segnalazione di informazioni durante l'esecuzione del codice: miglior design
Dire di avere il seguente (stupido, per essere semplice) caso. Userò Python.
def doStuff():
doStep1()
doStep2()
doStep3()
Ora, supponiamo si vuole dare un report delle varie fasi, se qualcosa va storto, ecc Non proprio debug: solo il comportamento informativo dell'applicazione.
Una prima soluzione semplice è quello di mettere le stampe
def doStuff():
print "starting doing stuff"
print "I am starting to do step 1"
doStep1()
print "I did step 1"
print "I am starting to do step 2"
doStep2()
print "I did step 2"
print "I am starting to do step 3"
doStep3()
print "I did step 3"
In generale, questo è piuttosto male. Supponiamo che questo codice finisca in una libreria. Non mi aspetterei che la mia biblioteca stampi qualcosa. Mi aspetterei che facesse il lavoro in silenzio. Tuttavia, a volte vorrei fornire informazioni, non solo in situazioni di debug, ma anche per mantenere l'utente informato che qualcosa è effettivamente in procinto di essere fatto. Anche la stampa è cattiva perché non hai il controllo sulla gestione dei tuoi messaggi. va solo allo stdout e non c'è nulla che tu possa fare al riguardo, eccetto il reindirizzamento.
Un'altra soluzione è avere un modulo per la registrazione.
def doStuff():
Logging.log("starting doing stuff")
Logging.log("I am starting to do step 1")
doStep1()
Logging.log("I did step 1")
Logging.log("I am starting to do step 2")
doStep2()
Logging.log("I did step 2")
Logging.log("I am starting to do step 3")
doStep3()
Logging.log("I did step 3")
Questo ha il vantaggio che si ordina di conoscere un luogo unico per il vostro servizio di registrazione, e si può sperimentare questo servizio tanto quanto si desidera. Puoi silenziarlo, reindirizzarlo su un file, su stdout o anche su una rete. Lo svantaggio è che si ottiene un accoppiamento molto forte con il modulo di registrazione. Fondamentalmente ogni parte del tuo codice dipende da questo e hai chiamate per accedere ovunque.
La terza opzione è quella di avere un oggetto report con un'interfaccia chiara, e si passa in giro
def doStuff(reporter=NullReporter()):
reporter.log("starting doing stuff")
reporter.log("I am starting to do step 1")
doStep1()
reporter.log("I did step 1")
reporter.log("I am starting to do step 2")
doStep2()
reporter.log("I did step 2")
reporter.log("I am starting to do step 3")
doStep3()
reporter.log("I did step 3")
Alla fine, si può anche passare l'oggetto reporter di doStepX() se hanno altro da dire. Vantaggio: riduce l'accoppiamento con un modulo, ma introduce l'accoppiamento con l'istanziazione dell'oggetto NullReporter. Questo può essere risolto utilizzando Nessuno come predefinita e il controllo prima di chiamare di registro, che è goffo, perché in pitone si deve scrivere un condizionale ogni volta (in C si potrebbe definire una macro)
def doStuff(reporter=None):
if reporter is not None:
reporter.log("starting doing stuff")
# etc...
Edit: altro l'opzione è lavorare in Qt e avere una strategia di segnale emit(). Mentre il codice viene eseguito, emette informazioni con i codici di stato appropriati e chiunque sia interessato può iscriversi ai segnali e fornire informazioni. Bello e pulito, molto disaccoppiato, ma richiede un po 'di codifica, in quanto non penso che questo possa essere fatto rapidamente con la batteria python inclusa.
Infine, è possibile generare eccezioni con un messaggio di errore significativo, ma questo ovviamente può essere utilizzato solo se si sta uscendo da una condizione di errore. non funziona per rapporti occasionali.
Modifica: Vorrei chiarire il fatto che la situazione è più generale e non limitata solo a una sequenza di passaggi invocati. potrebbe anche coinvolgere le strutture di controllo:
if disconnected:
print "Trying to connect"
connect()
else:
print "obtaining list of files from remote host"
getRemoteList()
Il rapporto potrebbe essere anche nelle routine reali, in modo si avrebbe un "stampa" nelle routine connect() e getRemoteList() come prima dichiarazione.
La domanda quindi sono:
- Cosa pensi sia il miglior design per un certo codice (in particolare nel caso di una libreria) di essere allo stesso tempo in silenzio quando il rumore potrebbe essere dirompente per il cliente , ma prolisso quando utile?
- Come gestire un intermix bilanciato tra codice logico e codice di reporting?
- Il mescolamento tra codice e controllo errori è stato risolto con eccezioni. Cosa si potrebbe fare per partizionare il "rumore" dei rapporti dalla logica del codice?
Modifica: più pensieri per la mente
penso che sia non solo una questione di disaccoppiamento del codice di registrazione dal codice logica. Penso che si tratti anche di disaccoppiare la produzione di informazioni dal consumo di informazioni. Esistono già tecniche simili, in particolare per gestire gli eventi dell'interfaccia utente, ma in realtà non vedo gli stessi pattern applicati al problema di registrazione.
Edit: ho accettato la risposta da Marcelo perché egli sottolinea in elementi di fatto che un compromesso è la soluzione migliore in questo caso, e non c'è pallottola d'argento. Tuttavia, tutti gli altri erano anche delle risposte interessanti e sono stato davvero lieto di revocarne tutti. Grazie per l'aiuto!
Ho aggiunto una taglia per vedere se è possibile approfondire la discussione su questa domanda. Inoltre, questa è la prima apertura della taglia, quindi ero anche curioso di farlo. –