Event Bus, Паттерны на практике, Unity, C#

Ойындар

Ссылка на гитхаб игры:
github.com/Haywaar/VerticalSc...
Ссылка на гитхаб классной но сложной реализации EventBus
github.com/PeturDarri/Generic...
Автору на кофе и шаурму
4276 5500 5792 8742 - карта Сбербанка
Если будут вопросы
мой тг @wargy
моя почта kazancev.s215@gmail.com
Тайминги:
00:00 Введение
00:26 Проблема зависимостей классов
02:00 Определение
03:13 Event bus как решение проблемы зависимостей классов
03:59 Техническая реализация Event Bus: введение
04:25 Event Bus: Kolhoznik Edition
05:24 Event Bus: Middle Edition
07:08 Kolhoznik vs Middle
08:57 Event Bus: Priority Events Edition
10:21 Другие разновидности Event Bus
11:21 Недостатки сигнальной шины
13:52 Service Locator + Signal Bus
14:42 Финал

Пікірлер: 79

  • @fairytale_black_cat
    @fairytale_black_cat20 күн бұрын

    Делал на этом паттерне большой проект, на прошлом месте работы. Там по сетевому протоколу приходили команды, а куча объектов в сцене должны были на них реагировать и менять свое состояни. Чтобы не плодить классы с событиями, обработчики регистрировались по имени события, а сами обработчики должны были принимать на входе один объект событие. Внутри события была коллекция параметров в словарике имя/значение. Поддерживался приоритет подписки и возможность рассылки событий глобально и внутри иерархии сцены. Не очень по ООП, но главное что работало и позволяло избегать зависимостей между объектами. Все что объекты в сцене знали, это один статик объект для работы с событиями.

  • @user-rj1bo4gw9o
    @user-rj1bo4gw9o Жыл бұрын

    Серёж, ты очень недооценённый автор образовательного контента, я как мидл и преподаватель в школе программирования очень рад что нашёл твой канал, сейчас я пересматриваю все твои ролики и наслаждаясь создаю по ним свои тестовые проекты в юнити.

  • @user-yn6np8xi7k
    @user-yn6np8xi7k Жыл бұрын

    Сергей, спасибо тебе за очередной вдумчивый и очень информативный урок!

  • @jasim1798
    @jasim1798 Жыл бұрын

    отличнейший канал, огромный рост - вопрос времени, спасибо за собранную и структурировано поданную инфу, и удачи в развитии канала

  • @user-tm6yc5gv3n
    @user-tm6yc5gv3n Жыл бұрын

    Очень круто рассказал! Благодарствую! Продолжай делать новые видео, здорово получается!

  • @alaleksandr.
    @alaleksandr.8 ай бұрын

    Короткое и информативное объяснение с примерами кода

  • @PinkPanteRus
    @PinkPanteRus11 ай бұрын

    Спасибо! Разъяснил от простого к сложному!

  • @user-de1wo4xd4j
    @user-de1wo4xd4j7 ай бұрын

    как же я орнул с базированного выражения (13:20): Нормально делай - нормально будет!

  • @pashafilenko1567
    @pashafilenko156711 ай бұрын

    Спасибо за видео!

  • @MrG12g
    @MrG12g Жыл бұрын

    очень крутое видео! молодец!

  • @user-cb7dk1ow3h
    @user-cb7dk1ow3h10 ай бұрын

    Серега лучший!

  • @sergeykazantsev1655

    @sergeykazantsev1655

    10 ай бұрын

    Вовка привет! Рад тебя видеть, как с гуся вода)

  • @alexsorokin8373
    @alexsorokin8373 Жыл бұрын

    очень интересный паттерн!) спасибо) Хочется сказать, что частично в практике этот паттерн в упрощенном виде начинает приходить в голову, когда много объектов начинают знать друг о друге...у меня сейчас есть ситуация в своем проекте, где я создал один класс, который собирает части игры - игрока, элементы UI, объекты некоторые и т.д и подписывает их друг на друга, по логике очень похоже на этот паттерн. И да - стоит сказать, что объекты теперь понятия друг о друге не имеют, ошибок меньше, но подписок становится так много, что контролировать это дело становится сложнее и сложнее, а вместо ошибок теперь надо разбирать почему где-то событие сработало два раза или почему некоторые действия идут в странной последовательности) Но кажется в программировании никогда не будет идеально решения)

  • @sergeykazantsev1655

    @sergeykazantsev1655

    Жыл бұрын

    ну да, тут надо балансировать, но если аккуратно писать, соблюдать SRP, может вполне хорошо получиться)

  • @gwynbleinn

    @gwynbleinn

    5 ай бұрын

    Согласен. Колхозная реализация мне пришла в голову на втором или третьем проекте. Как и некоторые другие паттерны. Но вот качество их исполнения стоит улучшать

  • @forcesoftheevil9252
    @forcesoftheevil9252 Жыл бұрын

    Спасибо за видео! Мне не нравится Event bus в двух версиях, так что я откажусь от подписок :D Но если серьёзно, почему бы шину событий разбивать на классы? Какие-нибудь PlayerEventBus, GameEventBus и прочее

  • @sergeykazantsev1655

    @sergeykazantsev1655

    Жыл бұрын

    Я бы лучше ивенты тогда разбивал. Типа Eventbus.PlayerEvents.PlayerDamaged += DoSomething() А насчёт подписок, она есть во всех трех версиях, просто в первой она спрятана)

  • @olza4967
    @olza496711 ай бұрын

    Отличный контент! Есть где нибудь реальные проекты посмотреть где реализованы различные паттерны?Вроде как говорят что сами Юнити не делают этого,что бы было как можно легче новичкам понимать. Спасибо.

  • @sergeykazantsev1655

    @sergeykazantsev1655

    11 ай бұрын

    Ну я вот этот скроллер на гитхаб выложил, можете посмотреть) Как раз рубрика "паттерны на практике" для этого и делалась) Юнити какие-то демки выкладывают, но я честно говоря не смотрел Хотя я у них на сайте нашёл бесплатную классную книжку, где сами Юнити рассказывают про паттерны, приводят примеры и тд resources.unity.com/games/level-up-your-code-with-game-programming-patterns

  • @gwynbleinn
    @gwynbleinn5 ай бұрын

    можно Сигнал тоже сделать дженериком. Наплодить от 1 до 5 таких классов с разным количеством полей. Подписчик ивента всё-равно всегда будет знать что он должен получить. Но имхо, падает уровень читаемости, плюс нужно будет изменить способ создания ключей

  • @sergeykazantsev1655

    @sergeykazantsev1655

    5 ай бұрын

    Можно

  • @Diyozen
    @Diyozen Жыл бұрын

    Хочется всё-таки сделать этот EventBus сервисом и зарегистрировать в сервис локаторе. А то чего он такой одинокий (хех) синглтон.

  • @sergeykazantsev1655

    @sergeykazantsev1655

    Жыл бұрын

    Поддерживаю! В своей игре, на гитхабе я в последней итерации именно так и сделал) я убрал статическую часть) в зенжекте он тоже не синглтоновый, кстати)

  • @kitoglav22
    @kitoglav223 ай бұрын

    Привет! Объясните, пожалуйста, неофиту что происходит в КолхозникЭдишн при объявлении класса? Вижу приватный конструктор, вижу геттер статический, но не понимаю для чего оно нужно. Чтобы видеть всех подписчиков? Почему мы не можем сделать статическими все Action и весь класс в целом?

  • @sergeykazantsev1655

    @sergeykazantsev1655

    3 ай бұрын

    Шина колхозника основана на паттерне(если его можно так назвать) синглтон, на 4:33 можете справа посмотреть как я триггерю ивенты и как я на них подписываюсь. Статический геттер нужен чтобы мы к классу сигнальной шины могли получить доступ из любого места.

  • @gamedev_renaissance1075
    @gamedev_renaissance107510 ай бұрын

    А вот к object это дело приводить обязательно? Выстрелить же может в Invoke, когда другой тип постараемся вызвать. (Из дженерик гитхаба код слишком перегруженный)

  • @sergeykazantsev1655

    @sergeykazantsev1655

    10 ай бұрын

    Насчёт приведения к object, скажу честно, я не нашёл другого решения. Тут же к object приводится для того, чтобы сохранить все Action в одном Dictionary, так, чтобы они хранились вне зависимости от того какие у них входные параметры. Если есть более хорошее решение - я с удовольствием с ним ознакомлюсь и переделаю код, потому что каст к object мне тоже не нравится. Насчёт выстрела Invoke - это можно пофиксить если сделать какой-нибудь пустой интерфейс ISignal и всем сигналам от него отнаследоваться. Впрочем, например в том же зенжекте никого это не парит и вы можете Invok-ать в тело что угодно.

  • @gamedev_renaissance1075

    @gamedev_renaissance1075

    10 ай бұрын

    @@sergeykazantsev1655 Да, просто я перебираю ваши "наивные" реализации и модифицирую под себя (респект за материал). Из-за замечательной контрвариантности Action сделать CallbackWithPriority у меня не вышло, а вот к object кастить - пожалуйста. Поэтому решил спросить.

  • @sergeykazantsev1655

    @sergeykazantsev1655

    10 ай бұрын

    Во-во и я другого решения не нашёл)

  • @chillcompany1028
    @chillcompany1028 Жыл бұрын

    Можно уточнить один момент? Вы всегда говорите "классы слушают", "классы подписываются" и т.д? Но ведь это делают не классы а непосредственно объекты. Это такое речевое допущение или я совсем запутался и понимаю всё не правильно?

  • @sergeykazantsev1655

    @sergeykazantsev1655

    Жыл бұрын

    Скорее моя оговорка. Объекты слушают и подписываются.

  • @esteticachannel4604

    @esteticachannel4604

    Жыл бұрын

    класс это и есть объект если что, это же ооп

  • @sergeykazantsev1655

    @sergeykazantsev1655

    10 ай бұрын

    Класс - тип, описывающий устройство объектов. Объект - это экземпляр класса. Класс можно сравнить с чертежом, по которому создаются объекты

  • @user-sn6xn1zx1v
    @user-sn6xn1zx1v4 ай бұрын

    Внимание, вопрос! :) Попытался внедрить шину сыбытий в своём проекте и столкнулся с проблемой. Как у тебя в проекте объекты с монобехами успеваются подписываться на сигналы. Поясню. Если учитываем, что код срабатывает последовательно, то может возникнуть ситуация, когда в одном скрипте создается сигнал, а во втором Start еще не отработал и подписка на сигнал не состоялась. Как быть? Или я что-то упустил? Пока я выкручиваюсь тем, что привязывают все нужные ситсемы друг к другу через Сервис Локатор и инициализирую в одном котроллере. То есть, использую Init вместо Start.

  • @sergeykazantsev1655

    @sergeykazantsev1655

    4 ай бұрын

    В завершающем видео по вертикальному скроллеру в конце(11:40) как раз с этим сталкивался. Вижу несколько вариантов: 1. Просто следовать правилам. Создать сигнальную шину в Awake, подписаться всеми классами в Start, стрелять сигналам строго после Start. Что собственно и осталось в этом проекте. Можно это рихтовать Script Execution Order или если есть Entry Point 2. Написать более сложную реализацию сигнальной шины с очередью, чтобы при подписке система проверяла не стрелял ли кто сигналами вот только что. Я такого решения не использовал но уверен что он есть.

  • @user-de1wo4xd4j
    @user-de1wo4xd4j7 ай бұрын

    В конце не совсем понял, а в чем принципиально лучше данные с игрока получать, а не с события?

  • @sergeykazantsev1655

    @sergeykazantsev1655

    7 ай бұрын

    А я что-то такое разве говорил? Оо

  • @user-de1wo4xd4j

    @user-de1wo4xd4j

    7 ай бұрын

    @@sergeykazantsev1655 14:31 :)

  • @sergeykazantsev1655

    @sergeykazantsev1655

    7 ай бұрын

    А, так в том конкретном случае нам надо было показать текущий и предыдущий счёт после события levelFinished. Можно конечно ивент с этими данными отправлять, но какой смысл если это нужно только в одном месте)

  • @esteticachannel4604
    @esteticachannel4604 Жыл бұрын

    вывод прост, используйте ecs

  • @sergeykazantsev1655

    @sergeykazantsev1655

    11 ай бұрын

    С моей точки зрения ecs не является универсальным решением на все случаи жизни) с ним много своих проблем)

  • @esteticachannel4604

    @esteticachannel4604

    11 ай бұрын

    @@sergeykazantsev1655 к примеру каких проблем?)

  • @sergeykazantsev1655

    @sergeykazantsev1655

    10 ай бұрын

    С моей точки зрения ECS предназначен только для игр/проектов где крайне важна оптимизация, скорость и производительность. Например игры с огромным количеством игровых сущностей(под миллион различных активных объектов на сцене), сетевые шутаны или те же MMORPG игры. Там DOTS даёт существенный бонус по производительности. Насчёт проблем - вот что знаю: сериализация данных и ресурсов, подгрузка бандлов и ресурсов. Недавно как раз смотрел конференцию игроделов где обсуждался ECS и каждая вторая фирма писала свой, а не использовала готовые решения так как сталкивалась с той самой сериализацией и подгрузкой ресурсов. А также излишне массивный код и непонятная парадигма на первое время для большинства начинающих разработчиков. Тот же match3 или вертикальный скроллер можно написать без ECS - пользователь разницы не увидит, но код будет написан быстрее и условному джуну/мидлу поддерживать его будет легче.

  • @esteticachannel4604

    @esteticachannel4604

    10 ай бұрын

    @@sergeykazantsev1655 странное мнение, оптимизация это лишь сайд эффект ецс, ецс же является первоначально архитектурным паттерном) дабы избавится от чрезмерной связности в коде и обеспечить гибкость, матч3 и скроллеры это что-то из разряда ГК где вообще архитектуру не соблюдают в принципе. DOTS это вообще не ецс, он там пришит сбоку. Погугли что значит ецс для начала и для чего он предназначен) подрузка бандлов и ресурсов, а также сериализация обеспечивается сервисами как раз таки со стороны ооп, это не проблема, гибридный подход в юнити пока не избежен, но всяко лучше чем потом спаггети легаси разгребать, а для подключения модуля нового какого 100500 зависимостей пробрасывать и думать какой бы паттерн тут бы всё зарешал

  • @sergeykazantsev1655

    @sergeykazantsev1655

    10 ай бұрын

    Могу я узнать на чём основаны такие заявления? Ты разрабатываешь игры на ECS? Если да, то для какого жанра, какой проект, как долго, какой фреймворк используешь(LeoEcs, Entitas или что-то другое) А если фреймворк свой - на чём он основан? Можешь ли ты скинуть статьи с хабра или откуда-то ещё про ECS которые конкретно ты считаешь базой и основой основ?

  • @user-wq2cl7hl1o
    @user-wq2cl7hl1o Жыл бұрын

    что значит выдаст enr? 1:21

  • @sergeykazantsev1655

    @sergeykazantsev1655

    Жыл бұрын

    NRE, Null reference exception

  • @user-wq2cl7hl1o

    @user-wq2cl7hl1o

    Жыл бұрын

    @@sergeykazantsev1655 спасибо!

  • @a.danilenko
    @a.danilenko11 ай бұрын

    1. В проекте используется ServiceLocator - это антипатерн. ServiceLocator это вариант DI с кучей проблем и недостатков. Рекомендую к изучению книгу Марка Симана "Внедрение зависимостей в .Net". Эта книга познакомит с лучшими практиками внедрения зависимостей. Может быть в каких-то простых проектах ServiceLocator допустим, но не в профессиональной командной разработке. Даже учитывая эти оговорки, что ServiceLocator это некая альтернатива DI, я не могу не указать на то, что в совокупности недостатков у ServiceLocator гораздо больше, чем достоинств. ServiceLocator это не альтернатива DI это и есть DI, но в одной из самых плохих реализаций. 2. EventBus это слишком "жирный" класс с точки зрения предоставляемого API - любой объект может подписаться на любое событие и вызвать любое событие. Это антипатерн. Такие решения тянут за собой много проблем, которые сразу видны опытному профессиональному разработчику. Интерфейс (в широком смысле слова) или API класса должен быть строго ограничен потребностями класса. Тут в одном абзаце всю проблематику не изложить. Если совсем кратко: нарушение принципов ISP и DI. 3. Подписки и отписки от событий неконтролируемы, соответственно могут происходить на любом этапе работы приложения. Отсюда следует опасность потенциальной проблемы, т.к. операции подписки и отписки не являются потокобезопасными. Соответственно, если требуется потокобезопасность при подписке и отписке, то следует использовать синхронизацию при доступе разделяемым ресурсам. 3. Часто встречаются мелкие недочёты в качестве кода: пренебрежение ключевым словом readonly, использование публичных полей вместо свойств и т.д. В общем, есть куда расти в качестве кода.

  • @sergeykazantsev1655

    @sergeykazantsev1655

    11 ай бұрын

    По поводу сервис локатора. На моём канале есть отдельное видео про сервис локатор где я говорю, что этот паттерн имеет свои недостатки и DI таки получше будет. В том же видео я говорю что SL может стать антипаттерном. А также есть подведение итогов, на котором я также говорю что сервис локатор многими дядями считается антипаттерном и у него есть проблема вседоступности. Критиковать сервис локатор в видео про сигнальную шину - крайне странно. Насчёт того что EventBus слишком "жирный" класс соглашусь, но как я понимаю тот же Zenject такая реализация вполне устраивает. Я согласен что на больших проектах будут проблемы, но без такой логики EventBus это не EventBus. Мне кажется что вседоступность ивентов это не такая большая беда, что как вседоступность классов как в том же SL. Но опять же говорю что да, наверное на больших проектах может вылезти Тайминги подписки и отписки и потокобезопасность да, в финальном видео по данной игре - я опять же говорил о том, что в некоторых случаях приходится это контролировать и с этим возникает головная боль. Очень интересно - где у меня публичные поля вместо свойств используются, покажите пожалуйста, мог реально промахнуться А расти можно бесконечно - я никогда не заявлял что в коде я преисполнился, что я Бог кода и тд

  • @a.danilenko

    @a.danilenko

    11 ай бұрын

    @@sergeykazantsev1655 Я не смотрел видео про ServiceLocator. ServiceLocator это и есть DI. Наверное, ты путаешь DI с DI-контейнером. DI и DI-контейнер это разные понятия. Публичные поля: классы CallbackWithPriority, ScoreChangedSignal, AddScoreSignal, SelectShipSignal и др.

  • @sergeykazantsev1655

    @sergeykazantsev1655

    11 ай бұрын

    Я знаю разницу между сервис локатором и di контейнером, и в том же видео я про это говорил За публичные поля спасибо

  • @a.danilenko

    @a.danilenko

    11 ай бұрын

    @@sergeykazantsev1655 Когда ты говоришь, что ServiceLocator это альтернатива DI, то ты имеешь виду, видимо, DI-контейнер. Вот я о чём. DI и DI-конейнер это не одно и тоже. Не следует их путать. ServiceLocator это тоже DI. Это тоже способ внедрения зависимостей. DI-контейнер и ServiceLocator это инструмент (вариант исполнения) используемый при DI. Крестовая отвертка не может быть альтернативой отвертке, быть лучше или хуже отвертки, поэтому что это тоже отвертка - вот такая аналогия, для понимания ситуации.

  • @sergeykazantsev1655

    @sergeykazantsev1655

    11 ай бұрын

    Под DI в этом случае я говорю в целом использование его в архитектуре, без DI-контейнера реализовать DI в проекте наверное можно, но зачем? Тот же Мартин Фаулер кстати в статье про SL сравнивает его именно с DI , но я думаю он тоже имеет DI как общую парадигму martinfowler.com/articles/injection.html

Келесі