Представьте, что вы разрабатываете API, в котором объекты, являющиеся частью системы, должны быть проинициализированы перед использованием.
Есть несколько способов простимулировать будущих пользователей этого API завершать инициализацию перед использованием:
- Ввести метод Initialize в каждом объекте, который нужно проинициаллизировать;
- Более "правильный" и следующий дизайну .Net framework - реализовать в классе ISupportInitialize;
- Пойти еще дальше и автоматизировать запуск и закрытие сессии инициализации;
- собственно объект, который необходимо проинициаллизировать (реализует ISupportInitialize);
- обертка, с помощью которой и происходит неявное управление сессией инициаллизации;

Это маленькая обертка, которая при создании получает ссылку на инициаллизируемый объект и запускает сессию, вызовом метода ISupportInitialize.BeginInit(). И при детерминированном освобождении закрывает сессию вызовом метода ISupportInitialize.EndInit(). Я намеренно не стал реализовывать в этом классе Disposable паттерн целиком с тем, чтобы сессия закрывалась только явно, либо вызовом Dispose() обертки, либо EndInit() самого объекта.
Вот как может выглядеть API:

и использование его в коде:

Easy-peasy!
А вот как выглядел бы код без Initializer:

мое чувство прекрасного плачет =).
Все это конечно не отменяет проверок на наличие сессии в самом объекте, т.к. он должен разрешать редактировать себя только на протяжение последней, а в остальном мне кажется несколько упрощает код, избавляя его от 2х строчек =), зато визуально ограничивает рамки сессии, что на мой взгляд весьма удобно и лаконично.
UPD: поступила вполне обоснованная критика - трудно читаемый код, тяжело разобраться зачем создавать аккаунт и сразу же "диспозить" его. На это могу лишь ответить, что API в данном случае (и вообще, если применяется этот паттерн) должно подсказывать ЧТО оно возвращает, другими словами метод называется не AddAccount, a InitializeAndAddAccount. А еще лучше если бы он назывался CreateAccountInitializer =).
UPD#2:
Придумал как избавиться от неразберихи и нарушения принципов, сохранив красоту идеи.
Не надо делать никаких "conventions" в API. Надо дать возможность желающим писать так как они хотят и привыкли, а извращенцам вроде меня дать возможность "юзать" такие "извраты":

и пример:

как видите API ничуть не пострадало и осталось читабельным и ясным, и ничьи принципы не нарушены.
PS: И еще одно дополнение. Вся эта возня вокруг Disposable паттерна - это ничто иное как smart pointer для .Net! Те из вас, кто писал на С++ поймут о чем я.
Удачи!
Комментариев нет:
Отправить комментарий