#разбор_кода #unity #архитектура
Приветствую 👋
Я завершил разбор кода для игры с кубиками 🥳.
Видео доступно по ссылке ➡️
https://www.youtube.com/watch?v=xikR6XkeuKE
Приятного просмотра 😉
Приветствую 👋
Я завершил разбор кода для игры с кубиками 🥳.
Видео доступно по ссылке ➡️
https://www.youtube.com/watch?v=xikR6XkeuKE
Приятного просмотра 😉
YouTube
Геймдев. Разбор архитектурного подхода на примере игры для канала https://t.me/gamedev_unity3d
Разбор подхода к проектированию архитектуры игрового проекта в рамках образовательного канала по разработке игр на Unity 3D.
Разбираем:
- точка входа в код
- слои абстракции
- префабы и их организация
- жизненный цикл объектов.
Больше полезной информации…
Разбираем:
- точка входа в код
- слои абстракции
- префабы и их организация
- жизненный цикл объектов.
Больше полезной информации…
Приветствую, друзья👋 Как вы? Удалось посмотреть разбор и подготовить список вопросов?
Отдайте свой голос, какой из вариантов по стриму для вас более привлекателен 👇
Отдайте свой голос, какой из вариантов по стриму для вас более привлекателен 👇
Anonymous Poll
19%
Будни после 19:00 Мск
22%
Выходной
22%
НГ каникулы 🎄🥂🍾
36%
Любой из вариантов
Друзья, с наступающим Новым Годом 🥂🍾
Пусть он принесет вам рост и новые возможности 🙌
С 2024 годом 🎄
Пусть он принесет вам рост и новые возможности 🙌
С 2024 годом 🎄
Друзья, приветствую 👋
К сожалению, в связи с высокой загруженностью материал на канале появляется не с той частотой, как мне хотелось 😔
Из приятного: на текущий момент готовлю для вас статью, посвященную различию между реактивными объектами и делегатами ✍️.
Обещанный стрим постараюсь поставить на 17 - 18 февраля.
Хорошего дня!
К сожалению, в связи с высокой загруженностью материал на канале появляется не с той частотой, как мне хотелось 😔
Из приятного: на текущий момент готовлю для вас статью, посвященную различию между реактивными объектами и делегатами ✍️.
Обещанный стрим постараюсь поставить на 17 - 18 февраля.
Хорошего дня!
Друзья, приветствую 👋 Я часто слышал аргумент: "Зачем реактивщина, ведь тоже самое можно сделать на делегатах". Пока я готовлю статью на эту тему, предлагаю рассмотреть пример (код в комментарии) и выбрать ответ.
Final Results
71%
Все ок, сработают две подписки, т.к. делегат - это ref тип
21%
Не ок, делегат - это value тип, вторая подписка не сработает
8%
Свой вариант ответа
Приветствую 👋
Стрим планируем на это воскресенье (18.02) 16:00 Мск
Буду раз всех вас видеть
Стрим планируем на это воскресенье (18.02) 16:00 Мск
Буду раз всех вас видеть
Всем привет, напоминаю, что через 45 мин начинаем стрим, стрим будет проходить в этом канале.
#архитектура #делегаты #event #action #Unity3D
Реактивное программирование vs делегаты в C#, Unity 3D
Приветствую 👋
Сегодня говорим про реактивное программирование vs делегаты (delegate) в C#, Unity 3D.
Для начала поясню, почему в статье я рассматриваю именно delegate, а не event или Action.
В C# Action, event и delegate являются ключевыми элементами, используемыми для работы с методами как с объектами. Это позволяет реализовывать такие концепции, как обратные вызовы (callbacks) и событийно-ориентированное программирование.
Delegate является основой для Action и event, где
- Action предоставляет удобный способ использования делегатов без возвращаемого значения
- event использует делегаты, обеспечивая контролируемый способ подписки на уведомления и обработки событий.
Поэтому в статье я буду рассматривать именно delegate.
Но для начала приведу основные отличия delegate, Action, event:
- Delegate - это тип, который представляет ссылки на методы с определённой сигнатурой и возвращаемым типом.
Это означает, что мы можем использовать переменные delegate для хранения ссылок на методы.
Delegate обеспечивает возможность передачи методов как аргументов методам или в качестве типов возвращаемых значений.
Delegate поддерживает многоадресные вызовы, позволяя вызывать несколько методов подписанных на делегат (multicast делегаты).
- Action - это обобщённый делегат, который не возвращает значение (возвращаемый тип void) и может принимать от 0 до 16 параметров.
Action является удобным способом использования делегатов, не требуя объявлять новый тип делегата для каждой сигнатуры метода.
По сути, Action предназначен для ситуаций, когда нужно передать методы, не возвращающие значение.
- Event - это способ, с помощью которого класс или объект может предоставлять уведомления.
Итак event (события):
-используют делегаты для обработки уведомлений.
- реализует паттерн Observer, где издатель уведомляет подписчиков о том, что произошло определённое событие.
- ограничивают способность внешнего кода вызывать делегат. Внешний код может подписаться на событие или отписаться от него, но не может напрямую вызвать делегат события.
- добавляют уровень инкапсуляции к делегатам, обеспечивая более безопасную и управляемую модель подписки на уведомления.
❗Реактивное программирование и использование делегатов в C# представляют собой два различных, но похожих подхода к обработке и передаче данных.
Оба механизма имеют свои преимущества и недостатки, и выбор между ними зависит от конкретных требований к проекту.
продолжение 🔽
Реактивное программирование vs делегаты в C#, Unity 3D
Приветствую 👋
Сегодня говорим про реактивное программирование vs делегаты (delegate) в C#, Unity 3D.
Для начала поясню, почему в статье я рассматриваю именно delegate, а не event или Action.
В C# Action, event и delegate являются ключевыми элементами, используемыми для работы с методами как с объектами. Это позволяет реализовывать такие концепции, как обратные вызовы (callbacks) и событийно-ориентированное программирование.
Delegate является основой для Action и event, где
- Action предоставляет удобный способ использования делегатов без возвращаемого значения
- event использует делегаты, обеспечивая контролируемый способ подписки на уведомления и обработки событий.
Поэтому в статье я буду рассматривать именно delegate.
Но для начала приведу основные отличия delegate, Action, event:
- Delegate - это тип, который представляет ссылки на методы с определённой сигнатурой и возвращаемым типом.
Это означает, что мы можем использовать переменные delegate для хранения ссылок на методы.
Delegate обеспечивает возможность передачи методов как аргументов методам или в качестве типов возвращаемых значений.
Delegate поддерживает многоадресные вызовы, позволяя вызывать несколько методов подписанных на делегат (multicast делегаты).
- Action - это обобщённый делегат, который не возвращает значение (возвращаемый тип void) и может принимать от 0 до 16 параметров.
Action является удобным способом использования делегатов, не требуя объявлять новый тип делегата для каждой сигнатуры метода.
По сути, Action предназначен для ситуаций, когда нужно передать методы, не возвращающие значение.
- Event - это способ, с помощью которого класс или объект может предоставлять уведомления.
Итак event (события):
-используют делегаты для обработки уведомлений.
- реализует паттерн Observer, где издатель уведомляет подписчиков о том, что произошло определённое событие.
- ограничивают способность внешнего кода вызывать делегат. Внешний код может подписаться на событие или отписаться от него, но не может напрямую вызвать делегат события.
- добавляют уровень инкапсуляции к делегатам, обеспечивая более безопасную и управляемую модель подписки на уведомления.
❗Реактивное программирование и использование делегатов в C# представляют собой два различных, но похожих подхода к обработке и передаче данных.
Оба механизма имеют свои преимущества и недостатки, и выбор между ними зависит от конкретных требований к проекту.
продолжение 🔽
#архитектура #делегаты #event #action #Unity3D
Реактивное программирование vs делегаты в C#, Unity 3D продолжение 🔽
Рассмотрим ключевые моменты каждого подхода и выделим тонкости, связанные с делегатами.
Реактивное программирование - это парадигма, ориентированная на потоки данных и распространение изменений.
Это значит, что можно легко выразить статические или динамические потоки данных в программе, а также реагировать на их изменения.
Например, при изменении реактивных характеристик игрока в профиле, мы всегда можем подписаться на изменение нужных характеристик и сделать обработку детерминированной.
Также можно получить и использовать текущие значение реактивных характеристик, если этого требует наша логика.
В реактивном программировании необходимо помнить, как работает ReactiveProperty и ReactiveCommand. Краткий обзор.
При вдумчивом подходе в коде не будет никаких сюрпризов.
Вы сможете неразрывно писать код, даже когда логика требует выполнить реактивную команду и получить и обработать результат.
Подробнее тут.
Плюсы:
- Упрощение работы с потоками данных.
Реактивное программирование позволяет обрабатывать запросы, упрощая работу с потоками данных и их синхронизацию.
- Лучшая масштабируемость и отзывчивость.
Игры, написанные с использованием реактивного подхода, часто более отзывчивы и легче масштабируются благодаря эффективному управлению потоками данных.
Делегаты работают медленнее, когда выполняется множество подписок.
- Выразительность и лаконичность кода.
Реактивное программирование позволяет выражать сложные потоки данных и их взаимодействия более лаконично и наглядно.
Минусы:
- Для разработчиков может быть сложно освоить парадигму реактивного программирования и научиться думать в её категориях.
- Отладка реактивного подхода может быть более сложной, если мы будем задействовать асинхронщину.
Продолжение 🔽
Реактивное программирование vs делегаты в C#, Unity 3D продолжение 🔽
Рассмотрим ключевые моменты каждого подхода и выделим тонкости, связанные с делегатами.
Реактивное программирование - это парадигма, ориентированная на потоки данных и распространение изменений.
Это значит, что можно легко выразить статические или динамические потоки данных в программе, а также реагировать на их изменения.
Например, при изменении реактивных характеристик игрока в профиле, мы всегда можем подписаться на изменение нужных характеристик и сделать обработку детерминированной.
Также можно получить и использовать текущие значение реактивных характеристик, если этого требует наша логика.
В реактивном программировании необходимо помнить, как работает ReactiveProperty и ReactiveCommand. Краткий обзор.
При вдумчивом подходе в коде не будет никаких сюрпризов.
Вы сможете неразрывно писать код, даже когда логика требует выполнить реактивную команду и получить и обработать результат.
Подробнее тут.
Плюсы:
- Упрощение работы с потоками данных.
Реактивное программирование позволяет обрабатывать запросы, упрощая работу с потоками данных и их синхронизацию.
- Лучшая масштабируемость и отзывчивость.
Игры, написанные с использованием реактивного подхода, часто более отзывчивы и легче масштабируются благодаря эффективному управлению потоками данных.
Делегаты работают медленнее, когда выполняется множество подписок.
- Выразительность и лаконичность кода.
Реактивное программирование позволяет выражать сложные потоки данных и их взаимодействия более лаконично и наглядно.
Минусы:
- Для разработчиков может быть сложно освоить парадигму реактивного программирования и научиться думать в её категориях.
- Отладка реактивного подхода может быть более сложной, если мы будем задействовать асинхронщину.
Продолжение 🔽
#архитектура #делегаты #event #action #Unity3D
Реактивное программирование vs делегаты в C#, Unity 3D продолжение 🔽
Делегаты в C# - это типы, которые представляют собой ссылки на методы.
С их помощью можно передавать методы в качестве аргументов другим методам, что делает делегаты удобным средством для реализации обратных вызовов и событий.
Плюсы:
- Делегаты позволяют создавать гибкие и масштабируемые приложения благодаря возможности использования методов в качестве параметров.
- Multicast делегаты могут ссылаться сразу на несколько методов, что позволяет легко реализовывать паттерн Observer.
Минусы:
- Сложности с multicast делегатами.
Хотя multicast делегаты предоставляют мощные возможности, управление порядком вызова и обработка возвращаемых значений могут быть сложными.
- Ограниченность в сравнении с реактивным программированием.
Делегаты могут быть менее выразительными при работе с сложными потоками данных по сравнению с реактивным программированием.
- Неявные тонкости, которые могут запутать начинающих разработчиков.
Продолжение 🔽
Реактивное программирование vs делегаты в C#, Unity 3D продолжение 🔽
Делегаты в C# - это типы, которые представляют собой ссылки на методы.
С их помощью можно передавать методы в качестве аргументов другим методам, что делает делегаты удобным средством для реализации обратных вызовов и событий.
Плюсы:
- Делегаты позволяют создавать гибкие и масштабируемые приложения благодаря возможности использования методов в качестве параметров.
- Multicast делегаты могут ссылаться сразу на несколько методов, что позволяет легко реализовывать паттерн Observer.
Минусы:
- Сложности с multicast делегатами.
Хотя multicast делегаты предоставляют мощные возможности, управление порядком вызова и обработка возвращаемых значений могут быть сложными.
- Ограниченность в сравнении с реактивным программированием.
Делегаты могут быть менее выразительными при работе с сложными потоками данных по сравнению с реактивным программированием.
- Неявные тонкости, которые могут запутать начинающих разработчиков.
Продолжение 🔽
Теперь вернёмся к опросу
К сожалению, корректный корректно ответила меньшая часть опрашиваемых.
❗Поскольку делегат - это ref тип, большинство посчитало, что в вышеприведенном коде вторая подписка сработает после вызова dealDamage в методе PassDealDamageToAnotherLayer, но это не так.
Для понимания происходящего в коде, опишу принцип работы:
Когда мы передаём делегат dealDamage в метод PassDealDamageToAnotherLayer, происходит передача по значению.
Это означает, что внутри PassDealDamageToAnotherLayer используется копия ссылки на тот же список вызовов делегата, что и в оригинальном делегате dealDamage на момент передачи.
Все изменения, которые происходят с делегатом dealDamage после его передачи в PassDealDamageToAnotherLayer (например, добавление новых методов в список вызовов делегата),
не отражаются на копии, которая уже была передана и используется внутри метода PassDealDamageToAnotherLayer.
Продолжение 🔽
К сожалению, корректный корректно ответила меньшая часть опрашиваемых.
public class Root : BaseMonoBehaviour
{
private delegate void DealDamage();
private void Start()
{
// инстанцируем делегат с лямбдой - заглушкой
DealDamage dealDamage = () =>
{
Debug.Log("Deal damage stub");
};
// тут условно передаем делегат в слой, который отвечает за вызов делегата
PassDealDamageToAnotherLayer(dealDamage);
// подписываемся логикой на делегат
dealDamage += () =>
{
Debug.Log("Deal damage subscriber");
};
}
private void PassDealDamageToAnotherLayer(DealDamage dealDamage)
{
// через 5 секунд сделаем вызов делегата
Observable.Timer(TimeSpan.FromSeconds(5)).Subscribe(_ =>
{
dealDamage();
});
}
}
❗Поскольку делегат - это ref тип, большинство посчитало, что в вышеприведенном коде вторая подписка сработает после вызова dealDamage в методе PassDealDamageToAnotherLayer, но это не так.
Для понимания происходящего в коде, опишу принцип работы:
Когда мы передаём делегат dealDamage в метод PassDealDamageToAnotherLayer, происходит передача по значению.
Это означает, что внутри PassDealDamageToAnotherLayer используется копия ссылки на тот же список вызовов делегата, что и в оригинальном делегате dealDamage на момент передачи.
Все изменения, которые происходят с делегатом dealDamage после его передачи в PassDealDamageToAnotherLayer (например, добавление новых методов в список вызовов делегата),
не отражаются на копии, которая уже была передана и используется внутри метода PassDealDamageToAnotherLayer.
Продолжение 🔽
Чтобы решить данную проблему, небходимо либо перенести подписку на делегат до его передачи в PassDealDamageToAnotherLayer, либо изменить логику так, чтобы ссылка на делегат могла быть обновлена внутри PassDealDamageToAnotherLayer после добавления новых подписок.
Приведу пример альтернативной вариация корректно работающего кода, в котором ссылка на делегат обновляется постоянно:
Подытожу Выбор между реактивным программированием и делегатами в C# зависит от специфики задачи и глубины понимания каждого из подходов.
Реактивное программирование лучше подходит для игр, где важна работа с потоками данных и их обработка в реальном времени.
Делегаты предпочтительнее в ситуациях, где требуется гибкость в реализации обратных вызовов и событий.
Оба подхода имеют свои тонкости и сложности, особенно когда речь идет о multicast делегатах, что требует от разработчика глубокого понимания обоих механизмов для их эффективного использования.
Всем хороших выходных 🖐
Приведу пример альтернативной вариация корректно работающего кода, в котором ссылка на делегат обновляется постоянно:
public class Root : BaseMonoBehaviour
{
private delegate void DealDamage();
private DealDamage _dealDamage;
private void Start()
{
// инстанцируем делегат с лямбдой - заглушкой
_dealDamage = () =>
{
Debug.Log("Deal damage stub");
};
// тут условно передаем делегат в слой, который отвечает за вызов делегата
PassDealDamageToAnotherLayer();
// подписываемся логикой на делегат
_dealDamage += () =>
{
Debug.Log("Deal damage subscriber");
};
}
private void PassDealDamageToAnotherLayer()
{
// через 5 секунд сделаем вызов делегата
Observable.Timer(TimeSpan.FromSeconds(5)).Subscribe(_ =>
{
_dealDamage();
});
}
}
Подытожу Выбор между реактивным программированием и делегатами в C# зависит от специфики задачи и глубины понимания каждого из подходов.
Реактивное программирование лучше подходит для игр, где важна работа с потоками данных и их обработка в реальном времени.
Делегаты предпочтительнее в ситуациях, где требуется гибкость в реализации обратных вызовов и событий.
Оба подхода имеют свои тонкости и сложности, особенно когда речь идет о multicast делегатах, что требует от разработчика глубокого понимания обоих механизмов для их эффективного использования.
Всем хороших выходных 🖐
Всем привет 👋 Предлагаю принять участие в опросе! На собесах часто спрашивают ➡️ Сколько поколений (количество) использует GC в Unity? Вводные: используем il2cpp backend с выключенным Incremental GC.
Final Results
35%
не использует поколения
7%
1
26%
2
28%
3
4%
Свой вариант ответа
#GC #Unity3D
Приветствую 👋
Подведу итог опроса ➡️ Сколько поколений (количество) использует GC в Unity?
Вводные: используем il2cpp backend с выключенным Incremental GC.
Поясню, что такое поколения:
- Поколения (одно и более) означают, что к частям (фрагментам) кучи применяются критерии возраста.
Даже в алгоритме с одним поколением объекты сортируются по возрасту.
GC выполняет подчистку, в том числе опираясь на эти критерии.
- При отсутствии поколений GC все объекты обрабатываются одинаково, независимо от того, как долго они существовали в памяти.
❗В Unity при использовании il2cpp backend с выключенным Incremental GC используется Boehm-Demers-Weiser (BDW) garbage collector.
Алгоритм работы - Mark-and-Sweep.
Основные этапы работы алгоритма:
- Mark
Сборщик мусора начинает с набора объектов, называемых "корневыми" (Root).
Корневые объекты это те, которые напрямую доступны из стека (например, локальные переменные и параметры функций) или из глобальных переменных.
Сборщик мусора обходит все объекты, доступные из корней, следуя ссылкам между объектами. Каждый достижимый (доступный) объект отмечается как "живой".
Этот процесс продолжается рекурсивно, пока не будут найдены все объекты, доступные из корней.
- Sweep
Сборщик мусора просматривает все объекты в управляемой куче и ищет те, которые не были отмечены на этапе отметки.
Все неотмеченные объекты считаются "мусором" и удаляются, то есть память, которую они занимали, освобождается.
После завершения этапа очистки сборщик мусора снимает отметки с всех оставшихся объектов, чтобы подготовить их к следующему циклу сборки мусора.
Важные моменты в реализации BDW:
- Сканирование Root
- Трассировка объектов. Строит граф объектов и вычисляет их доступность
- Ведет подсчет ссылок на объекты. Требуется для того, чтобы определить, когда на объект больше никто не ссылается
- ❗Не поддерживает поколения
- Выполняет процедуру финализации для недоступных объектов
- Работает в режиме stop the world. Это означает, что выполнение кода приложения периодически полностью приостанавливается.
Поэтому правильным ответом на вопрос будет:
В Unity при использовании il2cpp backend с выключенным Incremental GC поколения не используются!
35% участников ответило правильно 🤝
Всем хорошего дня 👋
Приветствую 👋
Подведу итог опроса ➡️ Сколько поколений (количество) использует GC в Unity?
Вводные: используем il2cpp backend с выключенным Incremental GC.
Поясню, что такое поколения:
- Поколения (одно и более) означают, что к частям (фрагментам) кучи применяются критерии возраста.
Даже в алгоритме с одним поколением объекты сортируются по возрасту.
GC выполняет подчистку, в том числе опираясь на эти критерии.
- При отсутствии поколений GC все объекты обрабатываются одинаково, независимо от того, как долго они существовали в памяти.
❗В Unity при использовании il2cpp backend с выключенным Incremental GC используется Boehm-Demers-Weiser (BDW) garbage collector.
Алгоритм работы - Mark-and-Sweep.
Основные этапы работы алгоритма:
- Mark
Сборщик мусора начинает с набора объектов, называемых "корневыми" (Root).
Корневые объекты это те, которые напрямую доступны из стека (например, локальные переменные и параметры функций) или из глобальных переменных.
Сборщик мусора обходит все объекты, доступные из корней, следуя ссылкам между объектами. Каждый достижимый (доступный) объект отмечается как "живой".
Этот процесс продолжается рекурсивно, пока не будут найдены все объекты, доступные из корней.
- Sweep
Сборщик мусора просматривает все объекты в управляемой куче и ищет те, которые не были отмечены на этапе отметки.
Все неотмеченные объекты считаются "мусором" и удаляются, то есть память, которую они занимали, освобождается.
После завершения этапа очистки сборщик мусора снимает отметки с всех оставшихся объектов, чтобы подготовить их к следующему циклу сборки мусора.
Важные моменты в реализации BDW:
- Сканирование Root
- Трассировка объектов. Строит граф объектов и вычисляет их доступность
- Ведет подсчет ссылок на объекты. Требуется для того, чтобы определить, когда на объект больше никто не ссылается
- ❗Не поддерживает поколения
- Выполняет процедуру финализации для недоступных объектов
- Работает в режиме stop the world. Это означает, что выполнение кода приложения периодически полностью приостанавливается.
Поэтому правильным ответом на вопрос будет:
В Unity при использовании il2cpp backend с выключенным Incremental GC поколения не используются!
35% участников ответило правильно 🤝
Всем хорошего дня 👋
#архитектура #solid #новые_подходы #simple
Приветствую 👋
У меня для вас большой блок информации для осмысления и анализа.
Все, кто интересуется архитектурой приложений, знают и стараются опираться на принципы SOLID и паттерны проектирования.
Впервые принципы SOLID были представлены в 2000 году в статье Design Principles and Design Patterns Роберта Мартина, также известного как Дядюшка Боб.
https://web.archive.org/web/20150906155800/http://www.objectmentor.com/resources/articles/Principles_and_Patterns.pdf
С тех пор прошло: 24 года.
Паттерны проектирования были адаптированы в 1987 году под язык SmallTalk Кентом Бэком и Вардом Каннингемом.
https://c2.com/doc/oopsla87.html
С тех пор прошло: 37 лет.
Предлагаю рассмотреть альтернативный подход, базирующийся на Composition Tree и концепции реактивного программирования.
Подход направлен на улучшение масштабируемости, поддержания чистоты кода, учитывая прогресс современных языков.
❗При разработке серьезных проектов тех руководитель должен решать задачу многокритериальной оптимизации:
- минимизация сроков разработки
- сдерживание роста кривой сложности доработок
- сохранение максимального уровня чистоты кода (понятность, читабельность, разделение на слои, и т.д.)
- контролируемость технического долга
- рост компетентности сотрудников
- bus фактор в отделе разработки > 1.
Предлагаемый подход позволяет решить такую задачу.
Напомню ключевые моменты Composition Tree. ⬇️
Каждое приложение начинает свою работу с определенной точки, называемой точкой входа (Entry Point).
Из данной точки происходит дальнейшее разветвление и исполнение кода.
Структура этого разветвления похожа на дерево и называется Composition Tree.
Места, где происходит разветвление, называются вершинами/узлами.
Самая первая вершина - это Entry Point.
В вершинах дерева осуществляется создание или получение необходимых компонентов и зависимостей.
Здесь же устанавливаются связи между различными частями приложения, такими как бизнес-логика, пользовательский интерфейс, сервисы и прочее.
Каждая вершина в дереве включает в себя контекст, который содержит все необходимые зависимости для данного участка кода.
Узлы или вершины в дереве соединены между собой через их контексты.
Продолжение 👇
Приветствую 👋
У меня для вас большой блок информации для осмысления и анализа.
Все, кто интересуется архитектурой приложений, знают и стараются опираться на принципы SOLID и паттерны проектирования.
Впервые принципы SOLID были представлены в 2000 году в статье Design Principles and Design Patterns Роберта Мартина, также известного как Дядюшка Боб.
https://web.archive.org/web/20150906155800/http://www.objectmentor.com/resources/articles/Principles_and_Patterns.pdf
С тех пор прошло: 24 года.
Паттерны проектирования были адаптированы в 1987 году под язык SmallTalk Кентом Бэком и Вардом Каннингемом.
https://c2.com/doc/oopsla87.html
С тех пор прошло: 37 лет.
Предлагаю рассмотреть альтернативный подход, базирующийся на Composition Tree и концепции реактивного программирования.
Подход направлен на улучшение масштабируемости, поддержания чистоты кода, учитывая прогресс современных языков.
❗При разработке серьезных проектов тех руководитель должен решать задачу многокритериальной оптимизации:
- минимизация сроков разработки
- сдерживание роста кривой сложности доработок
- сохранение максимального уровня чистоты кода (понятность, читабельность, разделение на слои, и т.д.)
- контролируемость технического долга
- рост компетентности сотрудников
- bus фактор в отделе разработки > 1.
Предлагаемый подход позволяет решить такую задачу.
Напомню ключевые моменты Composition Tree. ⬇️
Каждое приложение начинает свою работу с определенной точки, называемой точкой входа (Entry Point).
Из данной точки происходит дальнейшее разветвление и исполнение кода.
Структура этого разветвления похожа на дерево и называется Composition Tree.
Места, где происходит разветвление, называются вершинами/узлами.
Самая первая вершина - это Entry Point.
В вершинах дерева осуществляется создание или получение необходимых компонентов и зависимостей.
Здесь же устанавливаются связи между различными частями приложения, такими как бизнес-логика, пользовательский интерфейс, сервисы и прочее.
Каждая вершина в дереве включает в себя контекст, который содержит все необходимые зависимости для данного участка кода.
Узлы или вершины в дереве соединены между собой через их контексты.
Продолжение 👇
#архитектура #solid #новые_подходы #simple
Итак рассмотрим подход на базе Composition Tree и концепции реактивного программирования.
❗Подход включает в себя 6 основных принципов, название каждого из которых образует аббревиатуру SIMPLE.
Рассмотрим каждый из принципов:
S - Принцип разделения на основе потоков данных (Stream-based Segregation Principle):
Потоки данных описываются как последовательности событий или значений, которые меняются со временем.
В реактивном программировании потоки данных играют ключевую роль.
Принцип разделения на основе потоков данных подразумевает, что классы в приложении должны быть организованы вокруг потоков данных, которые они обрабатывают, обеспечивая тем самым отзывчивость и простоту управления состоянием.
Основой могут быть реактивные объекты: ReactiveProperty, ReactiveCommand.
I - Независимость от интерфейсов в бизнес-логике (Interface Free In Business Logic Principle):
Бизнес-логика должна быть свободна от традиционных интерфейсов, предпочитая вместо этого изменения и подписки на потоки данных для взаимодействия классов.
Это облегчает динамичное взаимодействие между классами и улучшает отзывчивость приложения.
‼️При этом слой сервисов и внешних библиотек допускает использование интерфейсов.
M - Модульное разделение (Modular Decoupled Principle):
В приложении должно быть четкое разделению ответственности между различными модулями, аналогично принципу единственной ответственности из SOLID.
Это подразумевает независимость бизнес-логики, пользовательского интерфейса, сетевых запросов и других частей приложения.
P - Принцип проактивной предсказуемости (Proactive Predictability Principle):
Системы, построенные с использованием реактивного подхода, должны обеспечивать легкость предсказания поведения и взаимодействий потоков данных.
Это требует четкой структуризации и отслеживания зависимостей, что упрощает тестирование и отладку.
Все потоки данных должны иметь чёткое назначение и иметь только необходимую ответственность.
L - Независимость слоёв (Layered-Independent Principle):
Используется слоистый подход к архитектуре, в котором каждый слой (UI, бизнес-логика, данные и др.) работает независимо, что повышает модульность и упрощает тестирование.
Слой в архитектуре приложений - это логическое разделение классов приложения, которое обеспечивает разграничение функциональности.
Каждый слой отвечает за определенную область задач и обладает своей ответственностью в контексте многоуровневой архитектуры.
❗Основных слоёв - 7:
- Entity Layer (вершина дерева Composition Tree)
- Presentation Model Layer (слой бизнес-логики)
- View Layer (слой представления)
- Service Layer (слой сервисов)
- Communication Layer (слой связей)
- Content/Data Layer (слой контента/данных)
- State Layer (слой состояний).
Вспомогательными слоями могут выступать сторонние библиотеки.
E - Закрытая инкапсуляция (Enclosed Encapsulation Principle):
Методы и переменные классов бизнес-логики приложения должны быть максимально закрыты (с модификатором доступа "private").
Это снижает зависимость между различными частями кода и упрощает поддержку и изменение приложения.
Благодарю за внимание!
Жду ваши вопросы в комментариях!
Хороших выходных 🖐️
Итак рассмотрим подход на базе Composition Tree и концепции реактивного программирования.
❗Подход включает в себя 6 основных принципов, название каждого из которых образует аббревиатуру SIMPLE.
Рассмотрим каждый из принципов:
S - Принцип разделения на основе потоков данных (Stream-based Segregation Principle):
Потоки данных описываются как последовательности событий или значений, которые меняются со временем.
В реактивном программировании потоки данных играют ключевую роль.
Принцип разделения на основе потоков данных подразумевает, что классы в приложении должны быть организованы вокруг потоков данных, которые они обрабатывают, обеспечивая тем самым отзывчивость и простоту управления состоянием.
Основой могут быть реактивные объекты: ReactiveProperty, ReactiveCommand.
I - Независимость от интерфейсов в бизнес-логике (Interface Free In Business Logic Principle):
Бизнес-логика должна быть свободна от традиционных интерфейсов, предпочитая вместо этого изменения и подписки на потоки данных для взаимодействия классов.
Это облегчает динамичное взаимодействие между классами и улучшает отзывчивость приложения.
‼️При этом слой сервисов и внешних библиотек допускает использование интерфейсов.
M - Модульное разделение (Modular Decoupled Principle):
В приложении должно быть четкое разделению ответственности между различными модулями, аналогично принципу единственной ответственности из SOLID.
Это подразумевает независимость бизнес-логики, пользовательского интерфейса, сетевых запросов и других частей приложения.
P - Принцип проактивной предсказуемости (Proactive Predictability Principle):
Системы, построенные с использованием реактивного подхода, должны обеспечивать легкость предсказания поведения и взаимодействий потоков данных.
Это требует четкой структуризации и отслеживания зависимостей, что упрощает тестирование и отладку.
Все потоки данных должны иметь чёткое назначение и иметь только необходимую ответственность.
L - Независимость слоёв (Layered-Independent Principle):
Используется слоистый подход к архитектуре, в котором каждый слой (UI, бизнес-логика, данные и др.) работает независимо, что повышает модульность и упрощает тестирование.
Слой в архитектуре приложений - это логическое разделение классов приложения, которое обеспечивает разграничение функциональности.
Каждый слой отвечает за определенную область задач и обладает своей ответственностью в контексте многоуровневой архитектуры.
❗Основных слоёв - 7:
- Entity Layer (вершина дерева Composition Tree)
- Presentation Model Layer (слой бизнес-логики)
- View Layer (слой представления)
- Service Layer (слой сервисов)
- Communication Layer (слой связей)
- Content/Data Layer (слой контента/данных)
- State Layer (слой состояний).
Вспомогательными слоями могут выступать сторонние библиотеки.
E - Закрытая инкапсуляция (Enclosed Encapsulation Principle):
Методы и переменные классов бизнес-логики приложения должны быть максимально закрыты (с модификатором доступа "private").
Это снижает зависимость между различными частями кода и упрощает поддержку и изменение приложения.
Благодарю за внимание!
Жду ваши вопросы в комментариях!
Хороших выходных 🖐️
#архитектура
Всем привет 👋
По интенсивности постов в канале, вы могли догадаться, что, к сожалению, у меня сейчас очень сильная загруженность. Оживим канал вместе 🤝
Сегодня поговорим про стратегии разделения логики в игровых проектах: от тонкой к толстой бизнес-логике.
Логика представления - это та часть логики, которая неотъемлемо относится к функционированию этого представления.
Бизнес-логика - это набор правил и алгоритмов, которые описывают, как игра должна работать на уровне взаимодействия объектов, принятия решений и выполнения действий. Она отвечает за то, как игра реагирует на действия игрока, как меняется состояние игры в ответ на эти действия, и т.д.
Подчеркну, что в целом бизнес-логика может работать вообще без представлений.
‼️Важное различие между бизнес-логикой и логикой представления в том, что первая отвечает за правила и процессы, определяющие ход игры, а вторая - за то, как эта информация передаётся игроку в виде визуальных и интерфейсных элементов.
Самый простой пример:
Бизнес-логика: У персонажа уменьшилось здоровье на 10 единиц после атаки врага.
Логика представления: Этот факт отражается на экране в виде уменьшения полосы здоровья и вспышки красного цвета.
Рассмотрим еще некоторые примеры из жизни, которые не всегда являются однозначными.
Вам необходимо попасть из точки А в Б.
Закрыть потребность можно разными способами:
- Вариант 1 - такси;
- Вариант 2 - самостоятельная поездка за рулем.
Вариант 1. Поездка на такси.
Участники:
- Вы, как пассажир задаете точку А и Б;
- Автомобиль;
- Водитель такси - знает как управлять автомобилем, ему известен маршрут поездки.
Садясь в машину, через какое-то время вы оказываетесь в точке Б, считаем что вы полностью доверились водителю и в процессе поездки не влияли на то, как именно она осуществлялась.
В данном примере:
Роль бизнес-логики выполняет пассажир, определяя высокоуровневую задачу, задавая начальную и конечную точки маршрута.
Представление - такси + водитель. Для пассажира и сторонних наблюдателей - виден процесс - машина едет.
Логика представления - навыки водителя и скрытый от глаз механизм работы машины.
Вариант 2 - самостоятельная поездка за рулем.
Участники:
- Вы в роли водителя и пассажира;
- Автомобиль.
В таком случае вы отвечаете за перемещение, выполняя роль водителя и вникая в маршрут, чтобы прибыть в точку Б.
Также вам необходимо владеть навыками вождения и топографии.
Из примеров видно, что в слой представления может быть зашито разное количество логики.
В данном примере:
Машина - представление, имеющее меньше логики, в сравнении с примером, где вы пользуетесь услугами такси.
Садясь за руль в качестве водителя вы выполняете роль бизнес-логики, берёте на себя управление машиной, отвечаете за маршрут.
Толстая бизнес-логика включает в себя большинство или все игровые механики и правила, централизованно управляя поведением игры.
В данном подходе слой представления в основном отвечает за визуализацию и ввод, но не вмешивается в логические операции.
Тонкая бизнес-логика ограничивается минимальным набором действий, делегируя большую часть поведенческой логики слою представления.
Данный подход позволяет представлению адаптироваться и изменяться без необходимости переписывать бизнес-логику.
Продолжение 👇
Всем привет 👋
По интенсивности постов в канале, вы могли догадаться, что, к сожалению, у меня сейчас очень сильная загруженность. Оживим канал вместе 🤝
Сегодня поговорим про стратегии разделения логики в игровых проектах: от тонкой к толстой бизнес-логике.
Логика представления - это та часть логики, которая неотъемлемо относится к функционированию этого представления.
Бизнес-логика - это набор правил и алгоритмов, которые описывают, как игра должна работать на уровне взаимодействия объектов, принятия решений и выполнения действий. Она отвечает за то, как игра реагирует на действия игрока, как меняется состояние игры в ответ на эти действия, и т.д.
Подчеркну, что в целом бизнес-логика может работать вообще без представлений.
‼️Важное различие между бизнес-логикой и логикой представления в том, что первая отвечает за правила и процессы, определяющие ход игры, а вторая - за то, как эта информация передаётся игроку в виде визуальных и интерфейсных элементов.
Самый простой пример:
Бизнес-логика: У персонажа уменьшилось здоровье на 10 единиц после атаки врага.
Логика представления: Этот факт отражается на экране в виде уменьшения полосы здоровья и вспышки красного цвета.
Рассмотрим еще некоторые примеры из жизни, которые не всегда являются однозначными.
Вам необходимо попасть из точки А в Б.
Закрыть потребность можно разными способами:
- Вариант 1 - такси;
- Вариант 2 - самостоятельная поездка за рулем.
Вариант 1. Поездка на такси.
Участники:
- Вы, как пассажир задаете точку А и Б;
- Автомобиль;
- Водитель такси - знает как управлять автомобилем, ему известен маршрут поездки.
Садясь в машину, через какое-то время вы оказываетесь в точке Б, считаем что вы полностью доверились водителю и в процессе поездки не влияли на то, как именно она осуществлялась.
В данном примере:
Роль бизнес-логики выполняет пассажир, определяя высокоуровневую задачу, задавая начальную и конечную точки маршрута.
Представление - такси + водитель. Для пассажира и сторонних наблюдателей - виден процесс - машина едет.
Логика представления - навыки водителя и скрытый от глаз механизм работы машины.
Вариант 2 - самостоятельная поездка за рулем.
Участники:
- Вы в роли водителя и пассажира;
- Автомобиль.
В таком случае вы отвечаете за перемещение, выполняя роль водителя и вникая в маршрут, чтобы прибыть в точку Б.
Также вам необходимо владеть навыками вождения и топографии.
Из примеров видно, что в слой представления может быть зашито разное количество логики.
В данном примере:
Машина - представление, имеющее меньше логики, в сравнении с примером, где вы пользуетесь услугами такси.
Садясь за руль в качестве водителя вы выполняете роль бизнес-логики, берёте на себя управление машиной, отвечаете за маршрут.
Толстая бизнес-логика включает в себя большинство или все игровые механики и правила, централизованно управляя поведением игры.
В данном подходе слой представления в основном отвечает за визуализацию и ввод, но не вмешивается в логические операции.
Тонкая бизнес-логика ограничивается минимальным набором действий, делегируя большую часть поведенческой логики слою представления.
Данный подход позволяет представлению адаптироваться и изменяться без необходимости переписывать бизнес-логику.
Продолжение 👇
#архитектура
Рассмотрим еще один пример: два типа управления самолетом в игровом симуляторе:
- Полностью ручное управление самолетом = толстая бизнес-логика.
На стороне бизнес-логики вся ответственность за управление самолётом: взлёт, полёт, посадку и атаку.
Представление занимается только отображением и передачей команд от пользователя.
В данном случае бизнес-логика помимо описания работы игровой логики будет включать в себя часть, которая отвечает за работу представлений.
- Автоматическое управление самолетом = тонкая бизнес-логика.
Бизнес-логика передает базовые команды, такие как перемещение из точки А в точку Б или выполнение атаки.
Сложные механизмы управления, такие как автовзлёт, посадка, полёт через 2 точки реализованы на стороне представления.
Бизнес-логика при этом может больше концентрироваться на реализации логики игры, а не логике того, как должно работать представление.
👉 Существует мнение:
- принцип работы динамического объекта в игре всегда нужно разделять на бизнес-логику, которая полностью отвечает за поведение этого объекта и представление, которое отображает то, что в него приходит.
На практике такой подход не всегда удобен, иногда проще сделать более толстое представление, так как с ним гораздо проще работать в “песочнице” при построении MVP (см. пример с самолётом выше). При этом логика представления не должна являться частью бизнес-логики игры.
- толстая бизнес-логика легко переносится между проектами, так как она мало зависит от конкретного представления и интерфейса пользователя.
Однако на практике, в большинстве случаев получается, что каждый проект - уникален и такого рода перенос требует существенного времени.
- тонкая бизнес-логика требует значительных изменений при переносе, так как часть поведенческой логики тесно связана с конкретными элементами представления.
На практике, префабы с правильно подготовленными скриптами имеют хорошую переносимость между проектами и их проще тестировать.
‼️Правильное понимание контекста проекта и адаптации стратегий разработки под уникальные требования и цели каждой конкретной игры очень важно.
От этого зависит, как будет читаться код игры, насколько просто будет вносить изменения и интегрировать новые фичи.
⚠️ На мой взгляд во всём должен быть баланс, не стоит пытаться делать тонким слой представления только ради того, чтобы сделать его тонким и наоборот.
Именно вы, как разработчик/лид/тимлид/CTO вносите вклад в решение задачи успеха, качества, удобности разработки и развитии проекта.
Хорошей всем недели 💪.
Рассмотрим еще один пример: два типа управления самолетом в игровом симуляторе:
- Полностью ручное управление самолетом = толстая бизнес-логика.
На стороне бизнес-логики вся ответственность за управление самолётом: взлёт, полёт, посадку и атаку.
Представление занимается только отображением и передачей команд от пользователя.
В данном случае бизнес-логика помимо описания работы игровой логики будет включать в себя часть, которая отвечает за работу представлений.
- Автоматическое управление самолетом = тонкая бизнес-логика.
Бизнес-логика передает базовые команды, такие как перемещение из точки А в точку Б или выполнение атаки.
Сложные механизмы управления, такие как автовзлёт, посадка, полёт через 2 точки реализованы на стороне представления.
Бизнес-логика при этом может больше концентрироваться на реализации логики игры, а не логике того, как должно работать представление.
👉 Существует мнение:
- принцип работы динамического объекта в игре всегда нужно разделять на бизнес-логику, которая полностью отвечает за поведение этого объекта и представление, которое отображает то, что в него приходит.
На практике такой подход не всегда удобен, иногда проще сделать более толстое представление, так как с ним гораздо проще работать в “песочнице” при построении MVP (см. пример с самолётом выше). При этом логика представления не должна являться частью бизнес-логики игры.
- толстая бизнес-логика легко переносится между проектами, так как она мало зависит от конкретного представления и интерфейса пользователя.
Однако на практике, в большинстве случаев получается, что каждый проект - уникален и такого рода перенос требует существенного времени.
- тонкая бизнес-логика требует значительных изменений при переносе, так как часть поведенческой логики тесно связана с конкретными элементами представления.
На практике, префабы с правильно подготовленными скриптами имеют хорошую переносимость между проектами и их проще тестировать.
‼️Правильное понимание контекста проекта и адаптации стратегий разработки под уникальные требования и цели каждой конкретной игры очень важно.
От этого зависит, как будет читаться код игры, насколько просто будет вносить изменения и интегрировать новые фичи.
⚠️ На мой взгляд во всём должен быть баланс, не стоит пытаться делать тонким слой представления только ради того, чтобы сделать его тонким и наоборот.
Именно вы, как разработчик/лид/тимлид/CTO вносите вклад в решение задачи успеха, качества, удобности разработки и развитии проекта.
Хорошей всем недели 💪.
Выбираем тему публикации 👇
Final Results
27%
Портрет хорошего разработчика
27%
Стал лидом, не пишешь код, стоит ли беспокоится?
39%
Современный тренд отрицания знаний
8%
Свой вариант (в комментариях)