Мова йде не лише про POLA, а й про запобігання недійсного стану як можливого джерела помилок.
Подивимось, як ми можемо надати певні обмеження для вашого прикладу, не надаючи конкретної реалізації:
Перший крок: Не дозволяйте нічого викликати до відкриття файлу.
CreateDataFileInterface
+ OpenFile(filename : string) : DataFileInterface
DataFileInterface
+ SetHeaderString(header : string) : void
+ WriteDataLine(data : string) : void
+ SetTrailerString(trailer : string) : void
+ Close() : void
Тепер має бути очевидним, що CreateDataFileInterface.OpenFile
потрібно викликати для отримання DataFileInterface
екземпляра, куди можуть бути записані фактичні дані.
Другий крок: Переконайтеся, що заголовки та причепи завжди встановлені.
CreateDataFileInterface
+ OpenFile(filename : string, header: string, trailer : string) : DataFileInterface
DataFileInterface
+ WriteDataLine(data : string) : void
+ Close() : void
Тепер ви повинні надати всі необхідні параметри наперед, щоб отримати DataFileInterface
: ім'я файлу, заголовок та трейлер. Якщо рядок трейлера недоступний, поки не будуть записані всі рядки, ви також можете перемістити цей параметр у Close()
(можливо, перейменувавши метод на WriteTrailerAndClose()
), щоб файл принаймні не міг бути закінчений без рядка з трейлером.
Щоб відповісти на коментар:
Мені подобається розділення інтерфейсу. Але я схильний думати, що ваші пропозиції щодо примусового виконання (наприклад, WriteTrailerAndClose ()) опираються на порушення SRP. (Це те, з чим я неодноразово боровся, але ваша пропозиція здається можливим прикладом.) Як би ви відповіли?
Правда. Я не хотів більше концентруватися на прикладі, ніж потрібно, щоб зробити свою думку, але це гарне питання. У цьому випадку я думаю, що я б назвав це Finalize(trailer)
і стверджую, що це робить не надто багато. Написання трейлера та закриття - лише деталі реалізації. Але якщо ви не погоджуєтесь або маєте подібну ситуацію, коли це інакше, ось можливе рішення:
CreateDataFileInterface
+ OpenFile(filename : string, header : string) : IncompleteDataFileInterface
IncompleteDataFileInterface
+ WriteDataLine(data : string) : void
+ FinalizeWithTrailer(trailer : string) : CompleteDataFileInterface
CompleteDataFileInterface
+ Close()
Я б насправді цього не робив для цього прикладу, але він показує, як слід вести техніку.
До речі, я припускав, що методи насправді потрібно викликати в такому порядку, наприклад, щоб послідовно писати багато рядків. Якщо цього не потрібно, я б завжди віддав перевагу будівельнику, як це запропонував Бен Коттрел .