Sto provando a progettare un'interfaccia generica per l'enumerazione su un file di elenco. Ogni iterazione aprirà un file, consenti al codice che consuma di accedere al file per eseguire alcune attività, quindi chiude il file.
All'inizio questo sembrava un semplice schema di enumerazione, ma il codice consumante in realtà ha bisogno di conoscere lo stato di ciascun file sia dopo che è stato aperto sia dopo che è stato chiuso. Quindi, piuttosto che avere un singolo metodo MoveNext()
la mia interfaccia ha metodi OpenNextFile()
e CloseCurrentFile()
, ognuno dei quali può restituire uno stato di errore. Inoltre, CloseCurrentFile()
ha bisogno di un modo per segnalare se ogni file è stato salvato o meno quando è stato chiuso.
Ecco la mia interfaccia corrente.
public interface IFileEnumerator
{
//The following properties reflect the current state of the IFileEnumerator
FileEnumeratorStatus Status { get; }
string StatusMessage { get; }
string FileName { get; }
byte[] FileData { get; }
//Attempts to open the next file
FileEnumeratorStatus OpenNextFile();
//Closes the current file
FileEnumeratorStatus CloseCurrentFile();
}
public enum FileEnumeratorStatus
{
AtStart, //Files have not been opened
Opened, //File currently opened
Opened_Failed, //Failed to open file
Close_Saved, //File closed and saved
Closed_NotSaved, //File closed and not saved
Close_SaveFailed, //File closed saving failed
AtEnd //No more files
}
Ecco un esempio del codice che consuma:
//FileProcessor is a class which run a ProcesssJob on a set of files returned by a
//IFileEnumerator and returns a ProcesssingJobResult with results of all the files
public class FileProcessor
{
public ProcesssingJobResult RunJob(IFileEnumerator fileEnumerator, ProcesssJob job)
{
List<FileProcessingResult> resultList = new List<FileProcessingResult>();
while (fileEnumerator.OpenNextFile() != FileEnumeratorStatus.AtEnd)
{
FileProcessingResult currentFileResult = new FileProcessingResult();
currentFileResult.FileName = fileEnumerator.FileName;
if (fileEnumerator.Status == FileEnumeratorStatus.Opened)
{
currentFileResult.JobCompleted = job.RunJob(fileEnumerator.FileData);
var closureStatus = fileEnumerator.CloseCurrentFile();
if (closureStatus == FileEnumeratorStatus.Close_Saved)
currentFileResult.Saved = true;
else if (closureStatus == FileEnumeratorStatus.Closed_NotSaved)
currentFileResult.Saved = false;
else if (closureStatus == FileEnumeratorStatus.Close_SaveFailed)
{
currentFileResult.Saved = false;
currentFileResult.Message = fileEnumerator.StatusMessage;
}
}
else if (fileEnumerator.Status == FileEnumeratorStatus.Opened_Failed)
{
currentFileResult.JobCompleted = false;
currentFileResult.Message = fileEnumerator.StatusMessage;
currentFileResult.Saved = false;
}
resultList.Add(currentFileResult);
}
ProcesssingJobResult jobresult = new ProcesssingJobResult();
jobresult.TimeStamp = DateTime.Now.ToUniversalTime();
jobresult.Machine = System.Environment.MachineName;
jobresult.User = System.Environment.UserName;
jobresult.Results = resultList;
return jobresult;
}
}
//A generic task that can be run on a file
public interface ProcesssJob
{
bool RunJob(byte[] data);
}
//Result of processing one file
public class FileProcessingResult
{
public bool JobCompleted { get; set; }
public bool Saved { get; set; }
public string FileName { get; set; }
public string Message { get; set; }
}
//The overall result of processing a set of files
public class ProcesssingJobResult
{
public DateTime TimeStamp { get; set; }
public String Machine { get; set; }
public String User { get; set; }
public List<FileProcessingResult> Results { get; set; }
}
Forse questa è solo una questione di attenta documentazione, ma ho difficoltà a gestire tutti questi diversi stati possibili. Disegnare un diagramma di stato non aiuta in quanto è possibile passare da ogni stato a quasi ogni altro stato. Il codice di consumo deve chiamare CloseCurrentFile () se OpenNextFile () restituisce Open_Failed? Cosa succede se lo fa? Verrà restituito uno stato Chiuso anche se un file non è mai stato aperto? Se CloseCurrentFile () non è stato chiamato dopo OpenNextFile () dovrebbe essere chiuso automaticamente dalla prossima chiamata a OpenNextFile () o dovrebbe essere generata un'eccezione? Preferisco che qualcuno che implementa questa interfaccia non si debba preoccupare di ottenere tutto ciò giusto.
Il problema è molto più semplice se ho un singolo metodo OpenNextFile () che è responsabile sia della chiusura dell'ultimo file aperto, sia dell'apertura del file successivo. Tuttavia, questo singolo metodo deve riportare sia il risultato dell'apertura del file successivo, sia la chiusura dell'ultimo file. Anche questo diventa complicato per il consumo di codice poiché lo stato finale di ogni file non viene determinato fino alla seguente iterazione.
public interface IFileEnumerator_Alternate
{
string FileName { get; }
byte[] FileData { get; }
string StatusMessage {get;}
//Closes the previous file, and opens the next file
Result OpenNextFile();
}
public class Result
{
public bool CurrentFileOpened {get; set;}
public string CurrentFileName {get; set;}
public string CurrentFileStatusMessage {get; set;}
public string LastFileName {get; set;}
public bool LastFileClosedOk {get; set;}
public string LastFileClosedStatusMessage {get; set;}
public bool LastFileSaved {get; set;}
}
C'è un altro modo per vedere questo, o qualche altro modello di progettazione comune che è più adatto a quello che sto cercando di fare? Ecco un diagramma di attività che mostra il processo complessivo.