Ещё один важный(а какие они ещё бывают) шаблон в нашу копилку.
Наблюдатель - это незаменимый шаблон для построения лёгких малосвязанных систем, который может сильно упростить жизнь, с точки зрения архитектуры. Фактически мы уже сталкивались с применением наблюдателя, когда рассматривали новую систему ввода, но не заостряли на этом внимание. Очень часто его применяют при построении UI, подписывая обновление визуальной части на оповещения об изменениях в логике. В Unity есть несколько вариантов для реализации этого шаблона и, честно говоря, у неподготовленного пользователя они могут вызвать некоторую путаницу, так что рассмотрим все начиная с основ.
Грубо говоря, наблюдатель делит мир на два типа объектов: издатели и подписчики. Подписчики могут подписаться(и отписаться) на получение уведомления о событии от издателя, а издатель оповещает о событии всех подписавшихся. Таким образом, создаётся возможность взаимодействия между объектами без жёсткой связи между ними. Как всегда, больше теории можно найти в этой статье или этой главе книги Роберта Нистрома.
Основным механизмом, реализующим наблюдателя, служит ключевое слово delegate
, также для обеспечения большей безопасности Unity(если быть точнее в .Net) предоставляет объекты типа event
.
Можно сказать, что delegate
это тип переменной, которая указывает на функцию, а не на значение переменной. Если вы когда-либо слышали о C++, то фактически это указатель на функцию.
Второе свойство, которое стоит упомянуть, это возможность добавлять несколько указателей на функции, которые будут исполнены при вызове делегата, что делает его реализацией рассматриваемого в этой статье шаблона.
|
|
Результат выполнения:
|
|
Также следует познакомиться с классом System.Action
, который является не чем иным, как определением public delegate void Action();
и позволяет обойтись без определения делегата на функцию без возвращаемых параметров. В случае необходимости аргументы функции можно задать с помощью дженериков Action<T, …>
, это означает, что вы можете перечислить необходимое количество аргументов функции с заданными типами (см. код в следующем примере).
event
является надстройкой над делегатом и, в целях безопасности, блокирует установку делегата извне класса напрямую(внутри класса, в котором объявлен эвент, установка разрешается), предоставляя только доступ к функциям добавления-удаления подписчиков.
|
|
Результат выполнения:
|
|
Нетрудно заметить, что использование комбинации public static event Action print = delegate { };
может стать хорошей альтернативой синглетону, в случае когда вам достаточно механизма оповещения подписчиков.
Обязательно удаляйте не нужные подписки, так как это может помешать сборщику мусора и стать причиной утечек памяти.
Наблюдатель - это очень важный паттерн, который позволит уменьшить связанность объектов между собой и повысить гибкость, как всегда, это не дастся бесплатно и придётся пожертвовать очевидностью связей и состояния, однако эта гибкость может предоставить возможность разделения проекта на подмодули, что является важным моментом на больших проектах. Пока! =)