Ho scritto una piccola app per gestire una serie arbitraria di attività (ad es., chiamare uno sproc SQL e acquisire out-vars, eseguire un'altra app, eseguire un pacchetto SSIS) con dipendenze tra le attività. Ogni attività ha uno stato (in attesa di iniziare, non riuscito e in attesa di riprovare, non riuscito e abbandonato, riuscito, ecc.). Internamente, ogni oggetto Task
ha un campo del tipo TaskStatus
enum. Esiste un oggetto TaskManager
che avvia attività e monitora i loro successi e fallimenti e un Dashboard
WinForm che mostra lo stato di avanzamento.
Devo esporre lo stato di ogni Task
come enum in tutta l'app, o solo a TaskManager
, o esporre sempre solo il valore stringa? Il mio approccio è stato quello di esporre lo stato come enum, ma questo significa che non solo TaskManager
ma anche Dashboard
ha una dipendenza dai possibili valori per TaskStatus
.
Ho quasi postato questa domanda sulla revisione del codice, ma a conti fatti ritengo che si adatti meglio qui, è soprattutto una questione di principio.
Dashboard
interagisce solo con Task.Status
in due punti. Innanzitutto, il suo valore è visualizzato in un DataGridView
che riepiloga tutte le attività, e in realtà dovrebbe usare un valore di testo, se non altro per consentire valori più carini. In secondo luogo, i valori di stato vengono confrontati quando la colonna Stato è ordinata, e ovviamente ciò funzionerà in entrambi i modi.
TaskManager
è la parte di iffy. Ad esempio, un TaskManager
ha oggetti NumTasksRunning
, NumTasksComplete
e NumTasksWaiting
. In origine, con Task
s che esponeva i loro stati direttamente, ho inserito questa logica in TaskManager
:
public int NumTasksRunning { get { return _tasks.Count(t => t.IsEnabled && (t.Status == TaskStatus.Running || t.Status == TaskStatus.Aborting || t.Status == TaskStatus.FailedAwaitingRetry)); } }
public int NumTasksComplete { get { return _tasks.Count(t => t.IsEnabled && (t.Status == TaskStatus.Succeeded || t.Status == TaskStatus.FailedAbandoned)); } }
public int NumTasksWaiting { get { return _tasks.Count(t => t.IsEnabled && (t.Status == TaskStatus.Waiting)); } }
Potrei spostare questa logica in proprietà in Task
, proteggendo TaskManager
dalla necessità di conoscere l'enum:
public bool IsRunning { get { return _status == TaskStatus.Running || _status == TaskStatus.Aborting || _status == TaskStatus.FailedAwaitingRetry; } }
public bool IsComplete { get { return _status == TaskStatus.Succeeded || _status == TaskStatus.FailedAbandoned; } }
public bool IsWaiting { get { return _status == TaskStatus.Waiting; } }
Tuttavia, ci sono altri posti in cui il codice è molto più semplice se TaskManager
può vedere direttamente lo stato delle attività:
public void Abort(Exception ex)
{
State = TaskManagerState.Aborting;
foreach (Task task in _tasks)
if (task.Status == TaskStatus.Running && task.CanAbort)
task.Abort(ex);
}
Ovviamente potrei aggiungere una nuova proprietà a Task
, forse chiamata CanAbortNow
( CanAbort
significa che l'attività può essere annullata in linea di principio, separata), ma questo è solo uno dei vari punti di tocco. Se finisco con una proprietà "IsX" per ogni possibile valore di TaskStatus
, ho davvero reso il codice più facile da mantenere?
Questa app è per uso personale, quindi la scelta conta solo in quanto mi piacerebbe conoscere il modo "giusto" - se c'è consenso.