Применение паттерна Observer с использованием слабых ссылок при реализации ViewModel. Часть 1.

13 Сен
2011

В моем последнем проекте возникла следующая ситуация. Модель предметной области включала в себя множество однотипных «микрообъектов», агрегированных в составные «макрообъекты», многие свойства которых вычисляются с использованием агрегатных функции от свойств составляющих их «микрообъектов». Пользователь приложения взаимодействует именно с макрообъектами, причем они формируются только в ответ на запрос пользователя, т.е. их нельзя создать заранее, в то время как микрообъекты создаются и существуют в одном репозитории в течение всего сеанса работы. Бизнес-операции приложения включают в себя множественные изменения состава микрообъектов, входящих в макрообъекты. Приложение реализовывалось на WPF, и реализация привязки к данным «в лоб» (т.е. с использованием интерфейса INotifyPropertyChanged) приводила к некоторым неприятным последствиям:
  1. Если интерфейс пользователя обновляется при каждом атомарном изменении состава макрообъекта, то вычисление составных свойств объекта будет происходить слишком часто, это приводит к сильной потере производительности. Другое решение состоит в том, чтобы явно включать во все операции изменения вызов процедуры пересчета составных свойств. Этот подход требует от разработчика очень большой внимательности и чреват трудно обнаруживаемыми ошибками.
  2. Когда макрообъект подписывается на события об изменении свойств составляющих его микрообъектов, микрообъект хранит ссылку на макрообъект. Поскольку микрообъекты существуют все время жизни приложения, то сборщик мусора никогда не уничтожит макрообъект, пока он не удалит подписку на события. Это вынуждает «размазывать» архитектуру приложения, включая управление временем жизни макрообъектов с учетом их использования в пользовательском интерфейсе.
В то время как хочется добиться следующих эффектов:
  • Свойства составных макрообъектов должны пересчитываться (и уведомлять об этом соответствующие элементы интерфейса) только после завершения пользовательских действий
  • Неиспользуемые более макрообъекты должны автоматически удаляться из памяти при очередной сборке мусора
Добиться требуемых эффектов удалось с использованием следующих технических решений:
  • Когда макрообъекты подписываются на события об изменении микрообъектов, используются слабые ссылки. Таким образом, микрообъекты не держат ссылок на подписанные на их события макрообъекты, и, когда макрообъект больше не используется в интерфейсе, он уничтожается сборщиком мусора
  • Отложенное уведомление об изменении значения свойства.
  • Как только возникает необходимость уведомить наблюдателей об изменении значения свойства, делегат, генерирующий событие об изменении, ставится в очередь диспетчера приложения с минимальными приоритетом
  • Ленивое вычисление свойств составных объектов – свойства объектов вычисляются только тогда, когда они действительно требуются (например, при обработке уведомления об изменении и запросе значения из пользовательского интерфейса)
В следующих постах я расскажу о деталях этих решений.
По материалам Хабрахабр.



загрузка...

Комментарии:

Наверх