La produzione di output prevedibile per ogni input possibile è responsabilità di ciascun modulo. Ad esempio (in C #):
class Logger
{
public ITextWriter Writer { get; set; }
private uint counter;
/// <summary>
/// Writes message in special format and returns the number of total messages written
/// </summary>
public uint Debug(string message)
{
if (message == null) throw new ArgumentNullException("message");
if (Writer == null) throw new InvalidOperationException("Writer not set");
Writer.Write(string.Format("{0:HHmmss}: [DEBUG] {1}", DateTime.Now, message));
return ++counter;
}
}
Il modulo Logger
è abbastanza dettagliato, ma è in output - l'eccezione, la chiamata a una depedency e il valore restituito - è prevedibile e ovvia per ogni argomento e stato.
Ma un possibile caso d'uso mi dà fastidio. Cosa succede se Writer
è stato impostato su qualche strana implementazione che in qualche modo chiama il metodo Logger.Debug
su quello stesso oggetto? La prima possibile conseguenza è lo stackoverflow dovuto alla ricorsione infinita. Secondo: output imprevedibile e possibili bug. È ovvio che tale situazione deve essere controllata in qualche modo. Se vogliamo un output prevedibile per il nostro logger, dovrebbe controllare le doppie voci:
// ...
private bool enter;
public uint Debug(string message)
{
if (message == null) throw new ArgumentNullException("message");
if (Writer == null) throw new InvalidOperationException("Writer not set");
if (enter) throw new InvalidOperationException("Double-entry");
enter = true;
Writer.Write(string.Format("{0:HHmmss}: [DEBUG] {1}", DateTime.Now, message));
enter = false;
return ++counter;
}
// ...
E sembra che ogni chiamata esterna (una chiamata non ai propri componenti) debba essere avvolta con tali flag. Sembra pazzesco!
È scritto il codice? O è normale credere che il tuo sistema non abbia chiamate circolari? Mi sto perdendo qualcosa di importante? Per favore, consiglia.
Aggiorna
È ancora peggio, ragazzi:
// ...
private bool enter;
public uint Debug(string message)
{
if (message == null) throw new ArgumentNullException("message");
if (Writer == null) throw new InvalidOperationException("Writer not set");
if (enter) throw new InvalidOperationException("Double-entry");
try
{
enter = true;
Writer.Write(string.Format("{0:HHmmss}: [DEBUG] {1}", DateTime.Now, message));
}
finally
{
enter = false;
}
return ++counter;
}
// ...
Credo che prendere un'eccezione sia a carico del modulo di livello superiore (quello che li ha creati tutti), quindi le eccezioni devono seguire la loro strada. Ma lo stato dopo la chiamata non riuscita deve rimanere corretto. L'eccezione non è la fine del mondo e i moduli possono essere riutilizzati.