Non si tratta solo di POLA, ma anche di prevenire lo stato non valido come possibile fonte di bug.
Vediamo come possiamo fornire alcuni vincoli al tuo esempio senza fornire un'implementazione concreta:
Primo passaggio: non consentire che venga chiamato alcunché prima che un file sia stato aperto.
CreateDataFileInterface
+ OpenFile(filename : string) : DataFileInterface
DataFileInterface
+ SetHeaderString(header : string) : void
+ WriteDataLine(data : string) : void
+ SetTrailerString(trailer : string) : void
+ Close() : void
Ora dovrebbe essere ovvio che CreateDataFileInterface.OpenFile
deve essere chiamato per recuperare un'istanza DataFileInterface
, in cui i dati effettivi possono essere scritti.
Secondo passaggio: assicurati che le intestazioni e i rimorchi siano sempre impostati.
CreateDataFileInterface
+ OpenFile(filename : string, header: string, trailer : string) : DataFileInterface
DataFileInterface
+ WriteDataLine(data : string) : void
+ Close() : void
Ora devi fornire anticipatamente tutti i parametri richiesti per ottenere DataFileInterface
: nomefile, intestazione e trailer. Se la stringa del trailer non è disponibile fino a quando non vengono scritte tutte le righe, puoi anche spostare questo parametro su Close()
(eventualmente rinominando il metodo su WriteTrailerAndClose()
) in modo che il file non possa essere completato senza una stringa di trailer.
Per rispondere al commento:
I like separation of the interface. But I'm inclined to think that
your suggestion about enforcement (e.g. WriteTrailerAndClose()) is
verging on a violation of SRP. (This is something that I have
struggled with on a number of occasions, but your suggestion seems to
be a possible example.) How would you respond?
È vero. Non volevo concentrarmi più sull'esempio del necessario per chiarire il mio punto, ma è una buona domanda. In questo caso, penso che lo chiamerei Finalize(trailer)
e sostengo che non fa troppo. La scrittura del trailer e la chiusura sono semplici dettagli di implementazione. Ma se non sei d'accordo o hai una situazione simile in cui è diverso, ecco una possibile soluzione:
CreateDataFileInterface
+ OpenFile(filename : string, header : string) : IncompleteDataFileInterface
IncompleteDataFileInterface
+ WriteDataLine(data : string) : void
+ FinalizeWithTrailer(trailer : string) : CompleteDataFileInterface
CompleteDataFileInterface
+ Close()
In realtà non lo farei per questo esempio, ma mostra come portare avanti la tecnica di conseguenza.
A proposito, ho assunto che i metodi in realtà debbano essere chiamati in quest'ordine, ad esempio per scrivere sequenzialmente molte righe. Se questo non è richiesto, preferirei sempre un costruttore, come suggerito da Ben Cottrel .