Сергей, спасибо! Материал востребованный, подача безупречная! Скинул линк на ваш видос сокурсникам. Поржали с Лунтика. Хотел бы увидеть столь же усвояемый материал по DIP в реализации Zenject Ну и по остальным принципам SOLID в таком же стиле! Спасибо от сообщества!
Паттерн Observer считаю одним из самых востребованных, полезных и часто используемых во многих аспектах программирования (web, gamedev и т.д.). Спасибо за видео.
8:45 То есть мне каждому классу, который имплементирует интерфейс ISubject нужно каждый раз прописывать 3 метода, 2 из которых всегда повторяют проверку if и добавление в лист? И ещё сам лист? Я с ума не сойду, если у меня таких классов много? Можно конечно копировать, но это звучит не правильно
Если нужен "голый" наблюдатель (не через events), то с С#8 мы можем дать в интерфейс типовую реализацию методов. Вещь сомнительная, но если следить за тем, чтобы не просачивалась конкретика в интерфейс, то это можно сделать так. Методы здесь работают только с тем, что есть внутри интерфейса, инкапсуляция не нарушается, но с явной реализацией в интерфейсах нужно быть крайне осторожным. public interface ISubject { ICollection Observers { get; init; } void Attach(IObserver observer) { if(!Observers.Contains(observer)) Observers.Add(observer); } void Detach(IObserver observer) { if (Observers.Contains(observer)) Observers.Remove(observer); } void Notify(); } UPD: Notify все таки лучше оставить нереализованным.
Тут единственный вопрос в конце к методу UpdateUI у HealthBar. А не лучше его сделать приватным, чтобы только сам класс решал, когда и как он будет обновлять полоску здоровья? А то вдруг злой геймменеджер снаружи его ради прикола будет вызывать? Или есть какой-то смысл его оставить публичным?
Здравствуйте. Хотел задать вопрос. Насколько я понимаю функционал паттерна Observer как и использование событий в данном случае, можно заменить на реактивные свойства, которые реализованы в том же UniRx. Хотел спросить все ли правильно я понимаю.
Да, всё верно. UniRx это продолжение развития паттерна Observer. Там так же есть подписки, отписки, уведомления и реакции на уведомления, только они имеют более удобный вид. UniRx как я понимаю вообще на этом выстроил свою философию кода, со своим IObservable и подписками на потоки данных
получается современная версия паттерна это просто action в классе object и подписка/отписка нужного метода от него в классе observer? никих интерфейсов, ничего. правильно?
если я Вас правильно понимаю, то скорее всего да. В подписке и отписке на экшны интерфейс не нужен. Но тут очень важно понимать, что если Вы на собесе скажете, что паттерн Observer это просто action в классе Object, и там подписка и отписка нужного метода без интерфейсов - я думаю, собеседующий этот ответ не одобрит. Тут в Observer важен сам принцип - взаимодействия между классами через события. Реализуете ли вы его классически или через action-ы - это уже вторично)
Кстати, очень бы не помешали примеры по разным паттернам, но не в ООП, а в Дата-ориентированном программировании в Юнити. Не планируется ли случайно такого в ближайшее время?
Есть более удобный способ использования Наблюдателя без интерфейсов. - Создаём класс события, являющимся ScriptableObject. - Примерная реализация: [CreateAssetMenu] public class GameEvent : ScriptableObject { private List _listeners = new List(); public void Subscribe(Action newListener) { if (_listeners.Contains(newListener)) { Debug.LogError($"Попытка повторного добавления {newListener} в список подписчиков события {name}."); return; } _listeners.Add(newListener); } public void Unsubscribe(Action listener) { if (!_listeners.Contains(listener)) { Debug.LogError($"Попытка удалить несуществующего подписчика {listener} из списка подписчиков события {name}."); return; } _listeners.Remove(listener); } public void Publish() { if (_listeners.Count == 0) { Debug.LogError($"Попытка инициировать событие {name}, у которого нет подписчиков"); return; } for (int i = 0; i < _listeners.Count; i++) _listeners[i].Invoke(); } } - Для каждого события создаём отдельный экземпляр с уникальным именем в ассетах. - Ссылки на конкретный экземпляр должны быть у издателя и подписчика(ов). - Издатель просто вызывает Publish() когда нужно. Подписчик просто передаёт метод в Subscribe([имя метода]), который должен обрабатывать событие. - Для событий с аргументами можно расширить класс GameEvent. Это позволяет подписчикам знать только о событиях, игнорируя подробности о субъектах. Самому событию без разницы кто его публикует и обрабатывает.
Братан, хорош, давай, давай вперед! Контент в кайф, можно ещё? Вообще красавчик!
О, ребятушки с ExtremeCode пожаловали) Спасибо за комменты)
Сергей, спасибо! Материал востребованный, подача безупречная!
Скинул линк на ваш видос сокурсникам. Поржали с Лунтика.
Хотел бы увидеть столь же усвояемый материал по DIP в реализации Zenject
Ну и по остальным принципам SOLID в таком же стиле!
Спасибо от сообщества!
Спасибо, OCP уже выложил, DI тоже в планах)
По Zenject было бы интересно послушать. Но там отдельный плейлист нужен будет :)
Очень круто объясняете. Приятно слушать и смотреть, спасибо!
Паттерн Observer считаю одним из самых востребованных, полезных и часто используемых во многих аспектах программирования (web, gamedev и т.д.). Спасибо за видео.
Спасибо чел, я прям прозрел когда посмотрел твоё видео. У меня будето пазл в голове сложился. Спасибо!
Очень хорошо объясняете! Спасибо!
Классно, понятно, интересно, виртуозно
Повторение, мать учения!
12:06 Нынче вроде можно ивенты проверять на нуль одной строкой, вот так: Event?.Invoke()
Спасибо!
8:54 Интерфейс, как и абстрактный класс, позволяет использовать protected.
Это очень здорово, но здесь то это к чему?
@@sergeykazantsev1655, к "в интерфейсе приватные поля не создашь". private не создашь, protected создашь, что в сущности тоже самое.
тупа лайк под каждым видео, по твоим видосам можно и мидлом стать
8:45 То есть мне каждому классу, который имплементирует интерфейс ISubject нужно каждый раз прописывать 3 метода, 2 из которых всегда повторяют проверку if и добавление в лист? И ещё сам лист? Я с ума не сойду, если у меня таких классов много? Можно конечно копировать, но это звучит не правильно
Далее по видео я показал, куда Observer эволюционировал - на 12:20 можете посмотреть :)
Там компактнее получилось
Спасибо) @@sergeykazantsev1655
Если нужен "голый" наблюдатель (не через events), то с С#8 мы можем дать в интерфейс типовую реализацию методов. Вещь сомнительная, но если следить за тем, чтобы не просачивалась конкретика в интерфейс, то это можно сделать так. Методы здесь работают только с тем, что есть внутри интерфейса, инкапсуляция не нарушается, но с явной реализацией в интерфейсах нужно быть крайне осторожным.
public interface ISubject
{
ICollection Observers { get; init; }
void Attach(IObserver observer)
{
if(!Observers.Contains(observer)) Observers.Add(observer);
}
void Detach(IObserver observer)
{
if (Observers.Contains(observer)) Observers.Remove(observer);
}
void Notify();
}
UPD: Notify все таки лучше оставить нереализованным.
Привет, подскажи, пожалуйста, с помощью каких инструментов создаёшь такие прикольные анимированные UML'ки?
Я это делаю в гугловском аналоге PowerPoint - просто формы в Google Slides
а UnityEvent'ы тоже сюда подходят?
Да, на 11:10 как раз про это и говорил
Ничего не понятно, но подписалась))
Тут единственный вопрос в конце к методу UpdateUI у HealthBar. А не лучше его сделать приватным, чтобы только сам класс решал, когда и как он будет обновлять полоску здоровья? А то вдруг злой геймменеджер снаружи его ради прикола будет вызывать? Или есть какой-то смысл его оставить публичным?
на 9:07 я как раз сделал UpdateUI приватным, насколько я помню, разве нет?) Или вопрос про HealthChanged?
Здравствуйте. Хотел задать вопрос. Насколько я понимаю функционал паттерна Observer как и использование событий в данном случае, можно заменить на реактивные свойства, которые реализованы в том же UniRx. Хотел спросить все ли правильно я понимаю.
Да, всё верно. UniRx это продолжение развития паттерна Observer. Там так же есть подписки, отписки, уведомления и реакции на уведомления, только они имеют более удобный вид. UniRx как я понимаю вообще на этом выстроил свою философию кода, со своим IObservable и подписками на потоки данных
@@sergeykazantsev1655 Спасибо за ответ. Вы делаете очень крутой обучающий контент. Буду с нетерпением ждать от вас новых видео)
Мощно💪
получается современная версия паттерна это просто action в классе object и подписка/отписка нужного метода от него в классе observer? никих интерфейсов, ничего. правильно?
если я Вас правильно понимаю, то скорее всего да. В подписке и отписке на экшны интерфейс не нужен. Но тут очень важно понимать, что если Вы на собесе скажете, что паттерн Observer это просто action в классе Object, и там подписка и отписка нужного метода без интерфейсов - я думаю, собеседующий этот ответ не одобрит. Тут в Observer важен сам принцип - взаимодействия между классами через события. Реализуете ли вы его классически или через action-ы - это уже вторично)
@@sergeykazantsev1655 благодарю за ответ)
Кстати, очень бы не помешали примеры по разным паттернам, но не в ООП, а в Дата-ориентированном программировании в Юнити. Не планируется ли случайно такого в ближайшее время?
Планируется, но не в ближайшее время)
оч сложно в понимание та как дана просто теория. Хотелось бы один раз увидеть на практике правильную реализацию паттерна а так спасибо
на практике в чистом обсервере нет нужды так как есть action-ы, на 11:57 и 12:20 на правой стороне слайда можете посмотреть как это выглядит
Сегодня бежал с работы домой, не успел добежать и на пол пути реализовал интерфейс АйОбсёрвер. Inventory.attire.pants.SetDirty()
Шютка))
Да, главное аккуратнее работать с классом VokzalnayaShaurma, он прям мотивирует подобный интерфейс реализовать)
@@sergeykazantsev1655 =)
Есть более удобный способ использования Наблюдателя без интерфейсов.
- Создаём класс события, являющимся ScriptableObject.
- Примерная реализация:
[CreateAssetMenu]
public class GameEvent : ScriptableObject
{
private List _listeners = new List();
public void Subscribe(Action newListener)
{
if (_listeners.Contains(newListener))
{
Debug.LogError($"Попытка повторного добавления {newListener} в список подписчиков события {name}.");
return;
}
_listeners.Add(newListener);
}
public void Unsubscribe(Action listener)
{
if (!_listeners.Contains(listener))
{
Debug.LogError($"Попытка удалить несуществующего подписчика {listener} из списка подписчиков события {name}.");
return;
}
_listeners.Remove(listener);
}
public void Publish()
{
if (_listeners.Count == 0)
{
Debug.LogError($"Попытка инициировать событие {name}, у которого нет подписчиков");
return;
}
for (int i = 0; i < _listeners.Count; i++)
_listeners[i].Invoke();
}
}
- Для каждого события создаём отдельный экземпляр с уникальным именем в ассетах.
- Ссылки на конкретный экземпляр должны быть у издателя и подписчика(ов).
- Издатель просто вызывает Publish() когда нужно. Подписчик просто передаёт метод в Subscribe([имя метода]), который должен обрабатывать событие.
- Для событий с аргументами можно расширить класс GameEvent.
Это позволяет подписчикам знать только о событиях, игнорируя подробности о субъектах. Самому событию без разницы кто его публикует и обрабатывает.
Ну это сигнальная шина некая получается, у меня по EventBus даже видео на канале есть)