View(ctx).png
99.3 KB
Контекст View - иллюстрация к статье
Prefab (view).png
99.5 KB
Prefab View - иллюстрация к статье
Entity (prefab+view).png
172.5 KB
Entity Prefab View - иллюстрация к статье
GameDev: разработка на Unity3D pinned «Привет, друзья 🙂 Ниже анонс тем для ближайших публикаций 👇 Все они вместе с затронутыми ранее темами являются важными составляющими разработки игр. UPD: для удобства навигации заголовок рассмотренных тем дополнен ссылкой на публикацию 1 Деление…»
#архитектура #разбор_кода
Приветствую, друзья 👋
Сегодняшняя публикация посвящена видеоразбору кода в рамках предлагаемого мной подхода к проектированию архитектуры.
Код написан мной на базе задания, которое давали ранее.
Т.к. на текущий момент по плану публикаций мы разобрали 4 темы, акцент при разборе сделан именно на них:
- Деление игры на слои абстракции.
- С чего должен начинаться Unity проект. Точка входа в код.
- MonoBehaviour.
- Организация префабов.
Видео получилось продолжительное и увесистое, доступно по ссылке
Приятного просмотра 📺
ПС: Приносим извинения за качество звука, проработаем данный вопрос к следующим записям!
Приветствую, друзья 👋
Сегодняшняя публикация посвящена видеоразбору кода в рамках предлагаемого мной подхода к проектированию архитектуры.
Код написан мной на базе задания, которое давали ранее.
Т.к. на текущий момент по плану публикаций мы разобрали 4 темы, акцент при разборе сделан именно на них:
- Деление игры на слои абстракции.
- С чего должен начинаться Unity проект. Точка входа в код.
- MonoBehaviour.
- Организация префабов.
Видео получилось продолжительное и увесистое, доступно по ссылке
Приятного просмотра 📺
ПС: Приносим извинения за качество звука, проработаем данный вопрос к следующим записям!
Telegram
GameDev: разработка на Unity3D
📌 UPD: группа желающих сделать тестовое задание для разбора сформирована 💪 всем спасибо за участие 👍
p/s если вы хотели, поучаствовать, но не успели, напишите, что-нибудь придумаем.
Приветствую, друзья 👋
С началом новой недели!
Пока мы готовим очередной…
p/s если вы хотели, поучаствовать, но не успели, напишите, что-нибудь придумаем.
Приветствую, друзья 👋
С началом новой недели!
Пока мы готовим очередной…
Приветствую! После публикации видео с разбором архитектурного подхода, от некоторых участников пришли комментарии, что объем слишком велик(. Что делаем?
Anonymous Poll
82%
оставьте на youtube, посмотрю там
18%
поделите на части и продублируйте здесь
#архитектура
Приветствую 👋
Сегодняшняя публикация посвящена жизненному циклу объектов (далее ЖЦ), в управлении которым важную роль играет класс Entity!
Роль Entity:
- создает и управляет объектами, в том числе ограничивает их жизненный цикл
- создает множество Entity, Pm, View
- создает реактивные объекты
- устанавливает связи через реактивные объекты
- создает и подготавливает ресурсы для объектов
- при завершении работы с объектами, освобождает ресурсы, занимаемые ими.
Каждая Entity может быть создана в подходящий момент в игре, например, когда игрок открывает сундук.
❗Созданные в Entity объекты являются частью ее жизненного цикла и могут использоваться только в рамках этого цикла.
Это означает, что после вызова метода Dispose у Entity, объекты, созданные ею, должны быть также освобождены и удалены из памяти, иначе могут возникнуть утечки.
❗Про освобождение памяти очень важно помнить, что при работе с Unity и другими движками, множество объектов создается и управляется автоматически.
Также контролировать ЖЦ и утечки помогает разделение слоя бизнес-логики и представления (о слоях говорили здесь).
Инстанцированные Entity создают дерево приложения - Composition Tree, которое определяет зависимости и взаимодействие объектов друг с другом (иллюстрация). Каждая Entity является вершиной этого дерева.
Подобный подход к управлению зависимостями похож на паттерн Composition Root, который рассматривается в различных статьях и книгах по проектированию приложений.
Подход предполагает, что в приложении существует единственная точка входа. В ней происходит инстанцирование и настройка всех объектов, затем они передаются в другие объекты через конструкторы или методы внедрения зависимостей.
Преимущества подхода
- гибкое и эффективное управление зависимостями
- обеспечение лучшей модульности
- упрощение поддержки и развития приложения, повышение его гибкости и надежности
- улучшение тестируемости кода.
Подобно Composition Root инстанцирование Entity в Composition Tree происходит в вершинах дерева.
Метод Framework'а DeferDispose, который существует в классах Entity, Pm и View, позволяет планировать вызов Dispose объекта, который будет передан в этом методе.
Метод DeferDispose(в классах Entity и Pm) вызывает Dispose как только в объекте, в котором вызывается этот метод, будет вызван Dispose.
Тогда как в классе MonoBehaviour(View) Dispose происходит при уничтожении GameObject, к которому этот MonoBehaviour прикреплен.
❗Использование метода DeferDispose наиболее рационально в конструкторе или в методе SetCtx (для MonoBehaviour), так как запланировать Dispose объекта можно в месте его создания.
Вне конструктора следует контролировать ЖЦ объекта самостоятельно, так как в некоторых случаях объект может быть создан чуть позже по требованию или не создан вовсе.
Внутри метода DeferDispose переданный объект добавляется в List. При вызове Dispose проход по List происходит в обратном порядке и вызывается Dispose для того объекта, который был передан в DeferDispose.
Проход в обратном порядке и Dispose всех объектов из списка связан с тем, что при выполнении Dispose некоторых объектов могут происходить различные операции, например, освобождение ресурсов, установка флагов или установка значений в реактивные объекты.
❌Dispose объектов в прямом порядке может привести к ошибкам, связанным с зависимостями между объектами, и некорректному освобождению ресурсов. Например, если один объект зависит от другого, то его Dispose должен произойти после Dispose зависимого объекта.
Подытожу: правильное управление ЖЦ объектов помогает избегать утечек памяти и строить более надежные и эффективные архитектуры приложений.
Жду ваши вопросы в комментариях👇
Приветствую 👋
Сегодняшняя публикация посвящена жизненному циклу объектов (далее ЖЦ), в управлении которым важную роль играет класс Entity!
Роль Entity:
- создает и управляет объектами, в том числе ограничивает их жизненный цикл
- создает множество Entity, Pm, View
- создает реактивные объекты
- устанавливает связи через реактивные объекты
- создает и подготавливает ресурсы для объектов
- при завершении работы с объектами, освобождает ресурсы, занимаемые ими.
Каждая Entity может быть создана в подходящий момент в игре, например, когда игрок открывает сундук.
❗Созданные в Entity объекты являются частью ее жизненного цикла и могут использоваться только в рамках этого цикла.
Это означает, что после вызова метода Dispose у Entity, объекты, созданные ею, должны быть также освобождены и удалены из памяти, иначе могут возникнуть утечки.
❗Про освобождение памяти очень важно помнить, что при работе с Unity и другими движками, множество объектов создается и управляется автоматически.
Также контролировать ЖЦ и утечки помогает разделение слоя бизнес-логики и представления (о слоях говорили здесь).
Инстанцированные Entity создают дерево приложения - Composition Tree, которое определяет зависимости и взаимодействие объектов друг с другом (иллюстрация). Каждая Entity является вершиной этого дерева.
Подобный подход к управлению зависимостями похож на паттерн Composition Root, который рассматривается в различных статьях и книгах по проектированию приложений.
Подход предполагает, что в приложении существует единственная точка входа. В ней происходит инстанцирование и настройка всех объектов, затем они передаются в другие объекты через конструкторы или методы внедрения зависимостей.
Преимущества подхода
- гибкое и эффективное управление зависимостями
- обеспечение лучшей модульности
- упрощение поддержки и развития приложения, повышение его гибкости и надежности
- улучшение тестируемости кода.
Подобно Composition Root инстанцирование Entity в Composition Tree происходит в вершинах дерева.
Метод Framework'а DeferDispose, который существует в классах Entity, Pm и View, позволяет планировать вызов Dispose объекта, который будет передан в этом методе.
Метод DeferDispose(в классах Entity и Pm) вызывает Dispose как только в объекте, в котором вызывается этот метод, будет вызван Dispose.
Тогда как в классе MonoBehaviour(View) Dispose происходит при уничтожении GameObject, к которому этот MonoBehaviour прикреплен.
❗Использование метода DeferDispose наиболее рационально в конструкторе или в методе SetCtx (для MonoBehaviour), так как запланировать Dispose объекта можно в месте его создания.
Вне конструктора следует контролировать ЖЦ объекта самостоятельно, так как в некоторых случаях объект может быть создан чуть позже по требованию или не создан вовсе.
Внутри метода DeferDispose переданный объект добавляется в List. При вызове Dispose проход по List происходит в обратном порядке и вызывается Dispose для того объекта, который был передан в DeferDispose.
Проход в обратном порядке и Dispose всех объектов из списка связан с тем, что при выполнении Dispose некоторых объектов могут происходить различные операции, например, освобождение ресурсов, установка флагов или установка значений в реактивные объекты.
❌Dispose объектов в прямом порядке может привести к ошибкам, связанным с зависимостями между объектами, и некорректному освобождению ресурсов. Например, если один объект зависит от другого, то его Dispose должен произойти после Dispose зависимого объекта.
Подытожу: правильное управление ЖЦ объектов помогает избегать утечек памяти и строить более надежные и эффективные архитектуры приложений.
Жду ваши вопросы в комментариях👇
#библиотека_программиста #composition_root
Приветствую 👋
Начнем неделю с подборки литературы, в которой рассматривается паттерн Composition Root:
📚
1. Книга, Mark Seemann, Dependency Injection in .NET, 2011
Краткий обзор: https://blog.ploeh.dk/2011/07/28/CompositionRoot/
2. Книга, Carl-Hugo Marcotte, Abdelhamid Zebdi, An Atypical "ASP.Net" Core 6 Design Patterns Guide, 2022
3. Книга, Gary Mclean, Adaptive Code via C#: Agile coding with design patterns and SOLID principles, 2014
4. Статья, Chris Fryer, Dependency Injection - Composition Root, 2018
https://medium.com/@cfryerdev/dependency-injection-composition-root-418a1bb19130
5. Статья, Jeremy, Dependency Injection Composition Root: Are We Just Moving the Coupling?, 2013
https://jeremybytes.blogspot.com/2013/03/dependency-injection-composition-root.html
Приятного чтения 🙂
И продуктивной недели 💪
Приветствую 👋
Начнем неделю с подборки литературы, в которой рассматривается паттерн Composition Root:
📚
1. Книга, Mark Seemann, Dependency Injection in .NET, 2011
Краткий обзор: https://blog.ploeh.dk/2011/07/28/CompositionRoot/
2. Книга, Carl-Hugo Marcotte, Abdelhamid Zebdi, An Atypical "ASP.Net" Core 6 Design Patterns Guide, 2022
3. Книга, Gary Mclean, Adaptive Code via C#: Agile coding with design patterns and SOLID principles, 2014
4. Статья, Chris Fryer, Dependency Injection - Composition Root, 2018
https://medium.com/@cfryerdev/dependency-injection-composition-root-418a1bb19130
5. Статья, Jeremy, Dependency Injection Composition Root: Are We Just Moving the Coupling?, 2013
https://jeremybytes.blogspot.com/2013/03/dependency-injection-composition-root.html
Приятного чтения 🙂
И продуктивной недели 💪
#архитектура #изолированность_классов
Приветствую 👋
Сегодня говорим про изолированность классов.
Изолированность классов - ситуация, в которой каждый класс:
- напрямую не зависит от другого
- не содержит публичные методы для обращения из другого места в приложении.
В данном контексте определение применимо к классам Entity, Pm (бизнес-логики) и View (представления).
Если вы пропустили публикацию про слои абстракции, ознакомиться можно тут.
Противоположный изолированности подход - наличие большого количества публичных методов и их вызов из разных мест в коде.
Большое количество публичных методов и их вызовы из разных мест в коде приводят к увеличению сложности кодовой базы и проблемам с сопровождением. Рано или поздно код превратится в “спагетти”.
"Спагетти код" - сложный в понимании и поддержке из-за большого количества взаимосвязанных частей и ветвлений логики код.
Для “спагетти” типична ситуация, когда изменения в одной части кода могут неожиданно привести к ошибкам а другой части кода.
Разберем зависимость между большим количеством публичных методов и качеством кода:
- нарушение принципа инкапсуляции и как следствие утечка абстракции.
Ситуация, при которой большое количество методов позволяет получить доступ к внутреннему состоянию объектов, приводит к тому, что в будущем изменение внутренней реализации класса потребует изменения всех методов, которые имеют доступ к этому состоянию.
- повышение сложности использования класса и усложнение интерфейса. Необходимость вызывать большое количество методов в определенном порядке и с определенными аргументами значительно усложнит использование данного класса в будущем.
- усложнение процесса отладки, тестирования и рефакторинга кода.
Ситуация, при которой есть возможность вызывать множество методов из разных мест в коде, усложняет локализацию ошибки, и увеличивает время ее исправление.
Избежать "спагетти кода" позволит:
- следование принципам SOLID и DRY
- разбиение приложения на слои абстракции
- сокращение количества публичных методов и удержание минимального количества связей между ними.
❗Изоляция классов друг от друга является важным принципом разработки, позволяет ослабить зацепление между классами, облегчить их тестирование и сопровождение.
Обеспечить изоляцию позволит использование минимального количества публичных методов и ограничение доступа к внутренней реализации класса.
Разберем, как организовать изоляцию классов👇
❗Итак изоляция классов обеспечивается через:
- для классов (не наследников MonoBehaviour) внедрения зависимостей через конструктор с использованием контекста
- для классов из слоя View (наследников MonoBehaviour) внедрение зависимостей также происходит через контекст, однако публичный метод SetCtx используется для передачи контекста в объект после его создания.
В идеальной ситуации SetCtx должен быть единственным публичным методом в классе, что позволит избежать проблем с неправильным использованием объекта.
💡Контекст должен содержать все необходимые зависимости, которые класс использует для своей работы, ❗ при этом класс не должен знать ничего о том, как и где создаются эти зависимости. Это позволяет легко заменять или изменять зависимости без изменения кода самого класса.
Важно, чтобы при передаче контекста все поля были инициализированы непустыми значениями. Это позволит избежать лишних проверок в коде (например на null).
❌ На практике распространена ситуация, когда до 20% кода класса состоит из проверок на пустые значения зависимостей, особенно когда речь идет про MonoBehaviour, где много логики написано в Update.
Таким образом, внедрение зависимостей через конструктор (или метод SetCtx для MonoBehaviour) и контекст позволит обеспечить изоляцию классов друг от друга, улучшит читаемость, тестируемость и сопровождаемость кода 💪
Прекрасного дня!
По традиции ждем вопросы в комментариях 👇
Приветствую 👋
Сегодня говорим про изолированность классов.
Изолированность классов - ситуация, в которой каждый класс:
- напрямую не зависит от другого
- не содержит публичные методы для обращения из другого места в приложении.
В данном контексте определение применимо к классам Entity, Pm (бизнес-логики) и View (представления).
Если вы пропустили публикацию про слои абстракции, ознакомиться можно тут.
Противоположный изолированности подход - наличие большого количества публичных методов и их вызов из разных мест в коде.
Большое количество публичных методов и их вызовы из разных мест в коде приводят к увеличению сложности кодовой базы и проблемам с сопровождением. Рано или поздно код превратится в “спагетти”.
"Спагетти код" - сложный в понимании и поддержке из-за большого количества взаимосвязанных частей и ветвлений логики код.
Для “спагетти” типична ситуация, когда изменения в одной части кода могут неожиданно привести к ошибкам а другой части кода.
Разберем зависимость между большим количеством публичных методов и качеством кода:
- нарушение принципа инкапсуляции и как следствие утечка абстракции.
Ситуация, при которой большое количество методов позволяет получить доступ к внутреннему состоянию объектов, приводит к тому, что в будущем изменение внутренней реализации класса потребует изменения всех методов, которые имеют доступ к этому состоянию.
- повышение сложности использования класса и усложнение интерфейса. Необходимость вызывать большое количество методов в определенном порядке и с определенными аргументами значительно усложнит использование данного класса в будущем.
- усложнение процесса отладки, тестирования и рефакторинга кода.
Ситуация, при которой есть возможность вызывать множество методов из разных мест в коде, усложняет локализацию ошибки, и увеличивает время ее исправление.
Избежать "спагетти кода" позволит:
- следование принципам SOLID и DRY
- разбиение приложения на слои абстракции
- сокращение количества публичных методов и удержание минимального количества связей между ними.
❗Изоляция классов друг от друга является важным принципом разработки, позволяет ослабить зацепление между классами, облегчить их тестирование и сопровождение.
Обеспечить изоляцию позволит использование минимального количества публичных методов и ограничение доступа к внутренней реализации класса.
Разберем, как организовать изоляцию классов👇
❗Итак изоляция классов обеспечивается через:
- для классов (не наследников MonoBehaviour) внедрения зависимостей через конструктор с использованием контекста
- для классов из слоя View (наследников MonoBehaviour) внедрение зависимостей также происходит через контекст, однако публичный метод SetCtx используется для передачи контекста в объект после его создания.
В идеальной ситуации SetCtx должен быть единственным публичным методом в классе, что позволит избежать проблем с неправильным использованием объекта.
💡Контекст должен содержать все необходимые зависимости, которые класс использует для своей работы, ❗ при этом класс не должен знать ничего о том, как и где создаются эти зависимости. Это позволяет легко заменять или изменять зависимости без изменения кода самого класса.
Важно, чтобы при передаче контекста все поля были инициализированы непустыми значениями. Это позволит избежать лишних проверок в коде (например на null).
❌ На практике распространена ситуация, когда до 20% кода класса состоит из проверок на пустые значения зависимостей, особенно когда речь идет про MonoBehaviour, где много логики написано в Update.
Таким образом, внедрение зависимостей через конструктор (или метод SetCtx для MonoBehaviour) и контекст позволит обеспечить изоляцию классов друг от друга, улучшит читаемость, тестируемость и сопровождаемость кода 💪
Прекрасного дня!
По традиции ждем вопросы в комментариях 👇
GameDev: разработка на Unity3D pinned «#архитектура #разбор_кода Приветствую, друзья 👋 Сегодняшняя публикация посвящена видеоразбору кода в рамках предлагаемого мной подхода к проектированию архитектуры. Код написан мной на базе задания, которое давали ранее. Т.к. на текущий момент по плану…»
Приветствую, друзья 👋
Вчера состоялось замечательное событие Yet Another Mobile Party или YAMP!
Было множество интересных докладчиков и докладов про мобильную разработку
В блоке, посвященном мобильному геймдеву (вы можете сразу перейти на него), где одним из приглашенных спикеров был я, обсудили текущее состояние индустрии, возможные тренды, стоит ли сейчас начинать свой путь в играх.
Полный выпуск по ссылке https://www.youtube.com/watch?v=dEKN4NYPs3M
Устраивайтесь поудобнее и приятного просмотра ☕
Вчера состоялось замечательное событие Yet Another Mobile Party или YAMP!
Было множество интересных докладчиков и докладов про мобильную разработку
В блоке, посвященном мобильному геймдеву (вы можете сразу перейти на него), где одним из приглашенных спикеров был я, обсудили текущее состояние индустрии, возможные тренды, стоит ли сейчас начинать свой путь в играх.
Полный выпуск по ссылке https://www.youtube.com/watch?v=dEKN4NYPs3M
Устраивайтесь поудобнее и приятного просмотра ☕
YouTube
Yet Another Mobile Party (YAMP) // 1 апреля 2023
Yet Another Mobile Party (YAMP) — это 5-ти часовой стрим в прямом эфире для iOS- и Android-разработчиков. Видеоподкаст без докладов и презентаций со спикерами из Яндекса и гостями из других компаний.
Вопросы можно задавать с хэштегом #вопрос в чате http…
Вопросы можно задавать с хэштегом #вопрос в чате http…
#разбор_кода #архитектура #конкурс
🔴 СТОП 🔴
За выходные у нас родилась идея провести розыгрыш книги: “Паттерны объектно-ориентированного проектирования, Влиссидес Джон, Хелм Ричард “. *
Для участия в розыгрыше необходимо выполнить задание 👇:
- Реализовать нанесение урона игроком врагу (делаем с одним врагом, но в голове держим, что их может быть много).
- Предусмотреть несколько зон попадания: голова, руки, торс, ноги.
- Попадание в каждую зону предусматривает разный урон.
- Максимальный HP врага - 100%.
Критерии оценки:
- декомпозиция
- сильная связность
- слабое зацепление
- дорабатываемость кода
- инкапсуляция кода
- разделение на слои абстракции
- графика - не важна.
(если вы с нами недавно, перед выполнением рекомендуем ознакомиться с рубрикатором, где мы рассматриваем данные темы).
❗Срок неделя, т.е. код ждем до 10.04 (следующий понедельник) до 12:00 по Москве).
Если по результатам выполнения, будет несколько претендентов на победу, победитель будет определен путем голосования в группе.
ℹ️ Желающие принять участие, плюсаните в комментарии либо в личку Ирине (t.me/IrinaZhi)
И конечно же все, принявшие участие, получат обратную связь.
Хорошего дня и продуктивной рабочей недели 💪
*Доставка силами Ozon.
🔴 СТОП 🔴
За выходные у нас родилась идея провести розыгрыш книги: “Паттерны объектно-ориентированного проектирования, Влиссидес Джон, Хелм Ричард “. *
Для участия в розыгрыше необходимо выполнить задание 👇:
- Реализовать нанесение урона игроком врагу (делаем с одним врагом, но в голове держим, что их может быть много).
- Предусмотреть несколько зон попадания: голова, руки, торс, ноги.
- Попадание в каждую зону предусматривает разный урон.
- Максимальный HP врага - 100%.
Критерии оценки:
- декомпозиция
- сильная связность
- слабое зацепление
- дорабатываемость кода
- инкапсуляция кода
- разделение на слои абстракции
- графика - не важна.
(если вы с нами недавно, перед выполнением рекомендуем ознакомиться с рубрикатором, где мы рассматриваем данные темы).
❗Срок неделя, т.е. код ждем до 10.04 (следующий понедельник) до 12:00 по Москве).
Если по результатам выполнения, будет несколько претендентов на победу, победитель будет определен путем голосования в группе.
ℹ️ Желающие принять участие, плюсаните в комментарии либо в личку Ирине (t.me/IrinaZhi)
И конечно же все, принявшие участие, получат обратную связь.
Хорошего дня и продуктивной рабочей недели 💪
*Доставка силами Ozon.
Приветствую 👋 Проверим знания или интуицию 😉 ➡️ Есть класс, одно из полей - структура. В конструкторе класса инициализируем структуру. Где будет находится структура после выхода из конструктора: на стеке или в куче? Будет ли при этом упаковка?
Final Results
23%
В куче, будет упаковка
34%
В куче, упаковки не будет
9%
На стеке, будет упаковка
24%
На стеке, упаковки не будет
10%
Пишите статьи, не задавайте вопросы
Приветствую 👋
Подведём итоги опроса -> Есть класс, одно из полей - структура. В конструкторе класса инициализируем структуру. Где будет находится структура после выхода из конструктора: на стеке или в куче? Будет ли при этом упаковка?
✅ Верный ответ (34% опрошенных) -> Структура будет находиться в куче, упаковки не будет
Но обо всем по порядку, давайте рассмотрим как распределились голоса 👇
В опросе приняли участие 119 человек:
33% считают, что после выхода из конструктора структура будет находится на стеке,
57% считают, что после выхода из конструктора структура будет находиться в куче.
Из выбравших вариант в куче
- 23% (от 119 голосов) - считают, что упаковка будет
- 34% (от 119 голосов)💪 - упаковки не будет
Как это работает и что происходит со структурой 👇
1. При выделении памяти под новый инстанс класса одним из детерминированных значений будет размер, требуемый на хранение данных этого класса. В это значение будет включена и структура, ее размер в памяти будет заранее известен.
2. Класс - это ссылочный тип, он будет инстанирован в куче. При выделении памяти в куче будет зарезервировано значение под структуру.
3. В конструкторе класса при создании и инициализации структуры, до момента из выхода из конструктора структура находится на стеке. Во время присвоения созданной структуры в поле класса происходит её копирование в кучу.
4. Никакой упаковки при этом не будет.
Благодарим за активность 👍
Всем хорошего дня 💪
Подведём итоги опроса -> Есть класс, одно из полей - структура. В конструкторе класса инициализируем структуру. Где будет находится структура после выхода из конструктора: на стеке или в куче? Будет ли при этом упаковка?
✅ Верный ответ (34% опрошенных) -> Структура будет находиться в куче, упаковки не будет
Но обо всем по порядку, давайте рассмотрим как распределились голоса 👇
В опросе приняли участие 119 человек:
33% считают, что после выхода из конструктора структура будет находится на стеке,
57% считают, что после выхода из конструктора структура будет находиться в куче.
Из выбравших вариант в куче
- 23% (от 119 голосов) - считают, что упаковка будет
- 34% (от 119 голосов)💪 - упаковки не будет
Как это работает и что происходит со структурой 👇
1. При выделении памяти под новый инстанс класса одним из детерминированных значений будет размер, требуемый на хранение данных этого класса. В это значение будет включена и структура, ее размер в памяти будет заранее известен.
2. Класс - это ссылочный тип, он будет инстанирован в куче. При выделении памяти в куче будет зарезервировано значение под структуру.
3. В конструкторе класса при создании и инициализации структуры, до момента из выхода из конструктора структура находится на стеке. Во время присвоения созданной структуры в поле класса происходит её копирование в кучу.
4. Никакой упаковки при этом не будет.
Благодарим за активность 👍
Всем хорошего дня 💪
#результаты
Приветствую, друзья 👋
Не так давно мы проводили конкурс, с призом - книга "Паттерны объектно-ориентированного проектирования", Влиссидес Джон, Хелм Ричард.
Спасибо всем участникам, победитель определен, им стал Артур Зайцев👏👏👏.
В коде Артура была максимально продумана:
- Реализация механики получения урона
- Выделение отдельного интерфейса для получения урона
- Передача информация об уроне.
Мы поздравляем Артура, желаем непрерывного роста и развития, ведь нет предела совершенству! И конечно же интересных и масштабных проектов 🥳💪
Вот, что Артур рассказал о себе 👇
В геймдеве я уже 6 лет, и только сейчас добрался до того, чтобы нормально засесть за пет проект. Начинал с VR проектов для бизнеса, потом был долгий период мобилок, и сейчас вернулся в VR, но уже делаю ПК игры.
Всем хорошего дня и продуктивной недели 💪
Приветствую, друзья 👋
Не так давно мы проводили конкурс, с призом - книга "Паттерны объектно-ориентированного проектирования", Влиссидес Джон, Хелм Ричард.
Спасибо всем участникам, победитель определен, им стал Артур Зайцев👏👏👏.
В коде Артура была максимально продумана:
- Реализация механики получения урона
- Выделение отдельного интерфейса для получения урона
- Передача информация об уроне.
Мы поздравляем Артура, желаем непрерывного роста и развития, ведь нет предела совершенству! И конечно же интересных и масштабных проектов 🥳💪
Вот, что Артур рассказал о себе 👇
В геймдеве я уже 6 лет, и только сейчас добрался до того, чтобы нормально засесть за пет проект. Начинал с VR проектов для бизнеса, потом был долгий период мобилок, и сейчас вернулся в VR, но уже делаю ПК игры.
Всем хорошего дня и продуктивной недели 💪
#архитектура #dependency_injection #di #zenject
Приветствую, друзья 👋
Сегодня говорим про Dependency Injection (DI) и DI фреймворки.
Статью дробим на 3 части:
1. Рассмотрим самый популярный фреймворк Zenject, обозначим недостатки.
2. Выделим достоинства Zenject и дадим общие рекомендации по выбору фреймворка.
3. Проиллюстрирую ряд обозначенных проблем примерами кода.
Итак, поехали 🚀
Самый распространенный и популярный DI фреймворк - Zenject, существует с 2014 года.
Zenject используется как независимыми разработчиками, так и крупными игровыми студиями, среди которых: Square Enix, Electronic Arts, Ubisoft, Zenimax Online Studios и многие другие.
Zenject - будет основной линией данной статьи, на нем будем приводить примеры.
Одной из критик Zenject является высокий уровень абстракции и высокий порог входа для новичков.
В процессе работы с Zenject я выделил ряд слабых мест, которые стоит учитывать при рассмотрении данного фреймворка:
1. отсутствие полного контроля над жизненным циклом объектов.
Zenject основывается на автоматическом внедрении зависимостей и может привести к утечкам памяти при некорректном уничтожении объектов
2. проблемы с настройкой очередностей в MonoInstaller Zenject контекста.
Zenject использует собственный механизм внедрения зависимостей и контроля жизненного цикла объектов, который может конфликтовать с механизмами Unity
3. усложнение настройки зависимостей и снижении производительности из-за создания большого количества объектов и вызова методов на каждый из них (особенно в коде с большим количеством зависимостей)
4. настройка проекта. Настройка складывается из двух частей: через GUI Unity и через код. Настройка через GUI опасна, так как может повредить код, что не всегда очевидно даже для опытных разработчиков. Исправление занимает много времени. Для новичков такая настройка особенно сложна и затруднительна в освоении
5. неявное внедрение зависимости через атрибут [Inject]
6. использование фабрик, предоставляемых Zenject, для создания экземпляров MonoBehaviour.
Подход не всегда оптимален, так как фабрики, предоставляемые Zenject, ограничены в функциональности и не могут обеспечить полную гибкость, доступную пользовательским решениям.
Недостатки фабрик, предоставляемых Zenject:
- невозможность создания нескольких экземпляров одного и того же компонента с различными параметрами
- ограниченная возможность создания экземпляров, которые требуют сложной логики инициализации
- ограничения на наследование MonoBehaviour
- ограничения, связанные с созданием экземпляров внутри сцены
7. усложнение процесса обновления Unity до новых версий из-за потенциальных несовместимостей с новыми API
8. портирование Zenject проекта на другие фреймворки.
Продолжение 👇
Приветствую, друзья 👋
Сегодня говорим про Dependency Injection (DI) и DI фреймворки.
Статью дробим на 3 части:
1. Рассмотрим самый популярный фреймворк Zenject, обозначим недостатки.
2. Выделим достоинства Zenject и дадим общие рекомендации по выбору фреймворка.
3. Проиллюстрирую ряд обозначенных проблем примерами кода.
Итак, поехали 🚀
Самый распространенный и популярный DI фреймворк - Zenject, существует с 2014 года.
Zenject используется как независимыми разработчиками, так и крупными игровыми студиями, среди которых: Square Enix, Electronic Arts, Ubisoft, Zenimax Online Studios и многие другие.
Zenject - будет основной линией данной статьи, на нем будем приводить примеры.
Одной из критик Zenject является высокий уровень абстракции и высокий порог входа для новичков.
В процессе работы с Zenject я выделил ряд слабых мест, которые стоит учитывать при рассмотрении данного фреймворка:
1. отсутствие полного контроля над жизненным циклом объектов.
Zenject основывается на автоматическом внедрении зависимостей и может привести к утечкам памяти при некорректном уничтожении объектов
2. проблемы с настройкой очередностей в MonoInstaller Zenject контекста.
Zenject использует собственный механизм внедрения зависимостей и контроля жизненного цикла объектов, который может конфликтовать с механизмами Unity
3. усложнение настройки зависимостей и снижении производительности из-за создания большого количества объектов и вызова методов на каждый из них (особенно в коде с большим количеством зависимостей)
4. настройка проекта. Настройка складывается из двух частей: через GUI Unity и через код. Настройка через GUI опасна, так как может повредить код, что не всегда очевидно даже для опытных разработчиков. Исправление занимает много времени. Для новичков такая настройка особенно сложна и затруднительна в освоении
5. неявное внедрение зависимости через атрибут [Inject]
6. использование фабрик, предоставляемых Zenject, для создания экземпляров MonoBehaviour.
Подход не всегда оптимален, так как фабрики, предоставляемые Zenject, ограничены в функциональности и не могут обеспечить полную гибкость, доступную пользовательским решениям.
Недостатки фабрик, предоставляемых Zenject:
- невозможность создания нескольких экземпляров одного и того же компонента с различными параметрами
- ограниченная возможность создания экземпляров, которые требуют сложной логики инициализации
- ограничения на наследование MonoBehaviour
- ограничения, связанные с созданием экземпляров внутри сцены
7. усложнение процесса обновления Unity до новых версий из-за потенциальных несовместимостей с новыми API
8. портирование Zenject проекта на другие фреймворки.
Продолжение 👇
#архитектура #dependency_injection #di #zenject
✅ Преимущества Zenject:
1. инверсия управления (IoC) и внедрение зависимостей (DI) помогают ослабить зацепление классов и улучшить тестируемость кода
2. поддержка скоупов (при условии их корректного использования) позволяет управлять жизненным циклом объектов и оптимизировать использование ресурсов
3. интеграция с Unity позволяет использовать DI в контексте игровых объектов и компонентов
4. использование интерфейсов в Zenject позволяет значительно повысить уровень абстракции в проекте.
Разделить проект на слои абстракции, каждый из которых будет содержать свои интерфейсы и реализации. Изменения в одном слое не будут затрагивать другие слои.
Кроме того, использование интерфейсов позволяет легко заменять реализации во время тестирования и создавать мок-объекты для модульного тестирования.
Zenject оптимален для слаженных и опытных командам с малой текучкой кадров и высоким уровнем компетенции.
Общие советы при выборе DI фреймворка:
- опирайтесь на потребности проекта и компетенцию команды разработчиков
- помните, использование DI фреймворков не всегда обязательно, зависит от целей проекта и его масштаба. Например, разработка прототипа не всегда требует использования DI фреймворка, так как направлена на быстрое создание простой модели продукта для проверки гипотез и его концепции
- основывайтесь на балансе между функциональностью фреймворка и сложностью его использования. Учитывайте особенности и преимущества, чтобы выбрать оптимальный для текущей задачи
- учитывайте наличие документации и наличие сообщества у выбранного фреймворка
- протестируйте выбранный DI фреймворк перед его использованием в реальном проекте.
Использование DI фреймворков может упростить разработку, облегчить поддержку и улучшить переносимость кода между разными проектами.
Жду вопросы в комментариях!
В следующей публикации приведу примеры-иллюстрации проблемных мест Zenject👇
✅ Преимущества Zenject:
1. инверсия управления (IoC) и внедрение зависимостей (DI) помогают ослабить зацепление классов и улучшить тестируемость кода
2. поддержка скоупов (при условии их корректного использования) позволяет управлять жизненным циклом объектов и оптимизировать использование ресурсов
3. интеграция с Unity позволяет использовать DI в контексте игровых объектов и компонентов
4. использование интерфейсов в Zenject позволяет значительно повысить уровень абстракции в проекте.
Разделить проект на слои абстракции, каждый из которых будет содержать свои интерфейсы и реализации. Изменения в одном слое не будут затрагивать другие слои.
Кроме того, использование интерфейсов позволяет легко заменять реализации во время тестирования и создавать мок-объекты для модульного тестирования.
Zenject оптимален для слаженных и опытных командам с малой текучкой кадров и высоким уровнем компетенции.
Общие советы при выборе DI фреймворка:
- опирайтесь на потребности проекта и компетенцию команды разработчиков
- помните, использование DI фреймворков не всегда обязательно, зависит от целей проекта и его масштаба. Например, разработка прототипа не всегда требует использования DI фреймворка, так как направлена на быстрое создание простой модели продукта для проверки гипотез и его концепции
- основывайтесь на балансе между функциональностью фреймворка и сложностью его использования. Учитывайте особенности и преимущества, чтобы выбрать оптимальный для текущей задачи
- учитывайте наличие документации и наличие сообщества у выбранного фреймворка
- протестируйте выбранный DI фреймворк перед его использованием в реальном проекте.
Использование DI фреймворков может упростить разработку, облегчить поддержку и улучшить переносимость кода между разными проектами.
Жду вопросы в комментариях!
В следующей публикации приведу примеры-иллюстрации проблемных мест Zenject👇
#архитектура #dependency_injection #di #zenject #код
Рассмотрим пример кода проблемных мест на базе Zenject обозначенных в предыдущей публикации.
Пример 1. Проблемы с производительностью
✅Одно из возможных решений проблемы производительности - использование объекта SomeDependency в виде синглтона, созданного во время запуска приложения, и использование его в качестве зависимости в SomeClass. Подход позволит избежать повторного создания объекта и улучшить производительность приложения.
Пример 2. Проблемы с циклической зависимостью
SomeDependency и SomeOtherDependency зациклены друг на друга, (т.е. SomeDependency требуется SomeOtherDependency и наоборот), при запуске приложения будет выброшено исключение об ошибке внедрения зависимостей.
Пример 3. Неявное внедрение зависимости через атрибут [Inject]
В результате, кодовая база может раздуться, так как для каждого интерфейса требуется создать реализацию, даже если это тривиальная реализация или если интерфейс используется только для DI.
Проблемы использования атрибута [Inject]:
1. Зависимости становятся скрытыми и сложными в управлении. Передаются неявно, что затрудняет понимание, какие зависимости в каких частях кода, и как их переиспользовать.
2. Ошибки времени выполнения.
В случае, если некоторые зависимости не были правильно настроены
3. Низкая гибкость. Сложно управлять жизненным циклом зависимостей, некоторые операции, например, уничтожение или замена зависимостей, могут быть затруднены.
4. Снижение производительности. В некоторых случаях требуются дополнительные операции по поиску и инициализации зависимостей.
Благодарим за внимание!
Хороших выходных 😉
Рассмотрим пример кода проблемных мест на базе Zenject обозначенных в предыдущей публикации.
Пример 1. Проблемы с производительностью
public class SomeClass
{
private readonly SomeDependency _dependency;
public SomeClass(SomeDependency dependency)
{
_dependency = dependency;
}
public void DoSomething()
{
// use _dependency
}
}
public class SomeDependency
{
public SomeDependency()
{
// do something expensive
}
}
public class SomeInstaller : MonoInstaller
{
public override void InstallBindings()
{
Container.Bind<SomeClass>().AsSingle().NonLazy();
Container.Bind<SomeDependency>().AsSingle().NonLazy();
}
}
В рассмотренном примере проблема с производительностью возникает при создании объектов с зависимостями во время выполнения программы. Конструирование объекта SomeDependency в SomeClass является дорогостоящей операцией, особенно если объект создается многократно в рамках работы приложения. ✅Одно из возможных решений проблемы производительности - использование объекта SomeDependency в виде синглтона, созданного во время запуска приложения, и использование его в качестве зависимости в SomeClass. Подход позволит избежать повторного создания объекта и улучшить производительность приложения.
Пример 2. Проблемы с циклической зависимостью
public class SomeClass : MonoBehaviour
{
[Inject] private SomeDependency _dependency;
private void Start()
{
_dependency.DoSomething();
}
}
public class SomeDependency
{
private readonly SomeOtherDependency _otherDependency;
public SomeDependency(SomeOtherDependency otherDependency)
{
_otherDependency = otherDependency;
}
public void DoSomething()
{
// use _otherDependency
}
}
public class SomeOtherDependency
{
public SomeOtherDependency(SomeDependency someDependency)
{
someDependency.DoSomething();
}
}
public class SomeInstaller : MonoInstaller
{
public override void InstallBindings()
{
Container.Bind<SomeDependency>().AsSingle();
Container.Bind<SomeOtherDependency>().AsSingle();
}
}
В примере наблюдаем проблему с циклической зависимостью между SomeDependency и SomeOtherDependency. SomeDependency и SomeOtherDependency зациклены друг на друга, (т.е. SomeDependency требуется SomeOtherDependency и наоборот), при запуске приложения будет выброшено исключение об ошибке внедрения зависимостей.
Пример 3. Неявное внедрение зависимости через атрибут [Inject]
public class MyComponent : MonoBehaviour
{
private IMyDependency _myDependency;
public void DoSomething()
{
_myDependency.DoStuff();
}
}
И интерфейс, который определяет эту зависимость:
public interface IMyDependency
{
void DoStuff();
}
То, чтобы внедрить зависимость в MyComponent, нужно добавить атрибут [Inject] к полю _myDependency:
public class MyComponent : MonoBehaviour
{
[Inject]
private IMyDependency _myDependency;
public void DoSomething()
{
_myDependency.DoStuff();
}
}
При использовании интерфейса IMyDependency необходимо создать и его реализацию. В результате, кодовая база может раздуться, так как для каждого интерфейса требуется создать реализацию, даже если это тривиальная реализация или если интерфейс используется только для DI.
Проблемы использования атрибута [Inject]:
1. Зависимости становятся скрытыми и сложными в управлении. Передаются неявно, что затрудняет понимание, какие зависимости в каких частях кода, и как их переиспользовать.
2. Ошибки времени выполнения.
В случае, если некоторые зависимости не были правильно настроены
3. Низкая гибкость. Сложно управлять жизненным циклом зависимостей, некоторые операции, например, уничтожение или замена зависимостей, могут быть затруднены.
4. Снижение производительности. В некоторых случаях требуются дополнительные операции по поиску и инициализации зависимостей.
Благодарим за внимание!
Хороших выходных 😉
Друзья 👋 Мы подготовили опрос и предлагаем поучаствовать ➡️Есть async метод, в нем выполняется долгая операция, не связанная с объектами Unity. В методе нет асинхронных вызовов и кода для явного запуска новых тредов. Что произойдет при await метода?
Final Results
18%
метод выполнится на главном треде, но чуть позже
43%
синхронно будет выполнен код метода
20%
метод будет обработан отдельным тредом из тред пула
19%
не знаю / требуется звонок другу ☎️
#ассинхронность #async #разбор_кода
Приветствую, друзья 👋
В рамках опроса про async метод, возник вопрос про асинхронность.
В данной публикации постараюсь развернуто ответить на заданный вопрос.
Поехали 🚀
Асинхронный код относится к стилю программирования, в котором задачи выполняются независимо друг от друга и не блокируют исполнение последующих инструкций до завершения.
Тогда как в традиционной синхронной модели программирования задачи выполняются последовательно: когда одна задача начинает выполнение, она блокирует выполнение других инструкций до завершения.
Если задача занимает много времени или ожидает ввода-вывода, мы можем столкнуться с замедлением работы программы.
Асинхронный код предлагает альтернативный подход: одна задача может выполняться параллельно с другими, что позволяет более эффективно использовать время процессора и других ресурсов
Вместо того, чтобы блокировать исполнение программы, асинхронный код передаст управление другим задачам и продолжит выполнение, когда ожидаемый результат будет доступен.
Асинхронный код часто используется там, где задачи, работающие в отдельных тредах, требуют ожидания результатов извне.
Например: выполнение сетевых запросов, чтение/запись файлов, обращение к базам данных и другие длительные операции ввода-вывода.
В языке программирования C# и фреймворке Unity асинхронное программирование можно реализовать с помощью ключевых слов async и await, а также с помощью использования корутин.
Эти механизмы позволяют реализовать асинхронный код более простым способом и повысить его читаемость, избегая явного управления потоками и блокировок.
В ходе дискуссии под опросом один из участников высказал предположение 👇:
Поясню, почему так сделать не получится.
UniTask - это обертка для того, чтобы можно было сделать кастомный await для типа.
UniTask не использует контекстов синхронизации и напрямую цепляется к PlayerLoop Unity.
Условия, при которых возможно применение await для типа:
- тип должен иметь public (или internal) метод GetAwaiter(), который должен вернуть тип ожидаемого объекта,
- ожидаемый объект должен реализовать интерфейс INotifyCompletion.
UniTask не содержит метод ConfigureAwait. ConfigureAwait содержит обычный Task и при его await можно сделать ConfigureAwait.
С UniTask достаточно сложно допустить ошибку с продолжением на разных потоках, так как после его await управление остается на главном потоке. В этом есть свои минусы, о которых я расскажу позже.
Что касается корутин.
Корутины в Unity - специальные функции, которые позволяют организовать асинхронное выполнение кода без использования множества потоков или сложных конструкций.
Корутины позволяют:
- делать паузы в выполнении кода,
- возвращать промежуточные результаты,
- выполнять итерации по определенным шагам.
Рассмотрим пример работы с корутинами в Unity 👇:
Продолжение в комментариях👇
Там же ждем ваши вопросы.
Всем хорошего дня👍
Приветствую, друзья 👋
В рамках опроса про async метод, возник вопрос про асинхронность.
В данной публикации постараюсь развернуто ответить на заданный вопрос.
Поехали 🚀
Асинхронный код относится к стилю программирования, в котором задачи выполняются независимо друг от друга и не блокируют исполнение последующих инструкций до завершения.
Тогда как в традиционной синхронной модели программирования задачи выполняются последовательно: когда одна задача начинает выполнение, она блокирует выполнение других инструкций до завершения.
Если задача занимает много времени или ожидает ввода-вывода, мы можем столкнуться с замедлением работы программы.
Асинхронный код предлагает альтернативный подход: одна задача может выполняться параллельно с другими, что позволяет более эффективно использовать время процессора и других ресурсов
Вместо того, чтобы блокировать исполнение программы, асинхронный код передаст управление другим задачам и продолжит выполнение, когда ожидаемый результат будет доступен.
Асинхронный код часто используется там, где задачи, работающие в отдельных тредах, требуют ожидания результатов извне.
Например: выполнение сетевых запросов, чтение/запись файлов, обращение к базам данных и другие длительные операции ввода-вывода.
В языке программирования C# и фреймворке Unity асинхронное программирование можно реализовать с помощью ключевых слов async и await, а также с помощью использования корутин.
Эти механизмы позволяют реализовать асинхронный код более простым способом и повысить его читаемость, избегая явного управления потоками и блокировок.
В ходе дискуссии под опросом один из участников высказал предположение 👇:
Где не найду описание, везде написано что поток может смениться после ожидания асинхронной операции.
Т.е. первая операция которая выполняется через await всё таки выполнится на главном потоке и только после этого может оказаться что код выполняется другим потоком?
...
//главный поток
await UniTask.WaitUntil(() => image.Source == icon).ConfigureAwait(false); //главный поток
//неизвестный поток(в том числе главный)
image.Source = icon; //возможна ошибка, если не главный поток
...
Поясню, почему так сделать не получится.
UniTask - это обертка для того, чтобы можно было сделать кастомный await для типа.
UniTask не использует контекстов синхронизации и напрямую цепляется к PlayerLoop Unity.
Условия, при которых возможно применение await для типа:
- тип должен иметь public (или internal) метод GetAwaiter(), который должен вернуть тип ожидаемого объекта,
- ожидаемый объект должен реализовать интерфейс INotifyCompletion.
UniTask не содержит метод ConfigureAwait. ConfigureAwait содержит обычный Task и при его await можно сделать ConfigureAwait.
С UniTask достаточно сложно допустить ошибку с продолжением на разных потоках, так как после его await управление остается на главном потоке. В этом есть свои минусы, о которых я расскажу позже.
Что касается корутин.
Корутины в Unity - специальные функции, которые позволяют организовать асинхронное выполнение кода без использования множества потоков или сложных конструкций.
Корутины позволяют:
- делать паузы в выполнении кода,
- возвращать промежуточные результаты,
- выполнять итерации по определенным шагам.
Рассмотрим пример работы с корутинами в Unity 👇:
Создадим новый скрипт и прикрепим его к объекту в сцене.
В созданном скрипте создадим функцию с ключевым словом IEnumerator. Это будет наша корутина.
IEnumerator MyCoroutine()
{
// Шаг 1
Debug.Log("Шаг 1");
yield return new WaitForSeconds(1f); // Пауза на 1 секунду
// Шаг 2
Debug.Log("Шаг 2");
yield return new WaitForSeconds(0.5f); // Пауза на 0.5 секунды
// Шаг 3
Debug.Log("Шаг 3");
yield return null; // Пауза на один кадр
// Шаг 4
Debug.Log("Шаг 4");
}
Вызовем корутину с помощью функции StartCoroutine в методе Start.
Продолжение в комментариях👇
Там же ждем ваши вопросы.
Всем хорошего дня👍