惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
WordPress大学
WordPress大学
N
Netflix TechBlog - Medium
宝玉的分享
宝玉的分享
V
Visual Studio Blog
S
Securelist
P
Palo Alto Networks Blog
A
Arctic Wolf
T
Tor Project blog
P
Proofpoint News Feed
I
InfoQ
博客园 - 三生石上(FineUI控件)
T
Threat Research - Cisco Blogs
G
GRAHAM CLULEY
M
MIT News - Artificial intelligence
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
Microsoft Security Blog
Microsoft Security Blog
MongoDB | Blog
MongoDB | Blog
C
CXSECURITY Database RSS Feed - CXSecurity.com
Apple Machine Learning Research
Apple Machine Learning Research
S
Secure Thoughts
Cyberwarzone
Cyberwarzone
Blog — PlanetScale
Blog — PlanetScale
Simon Willison's Weblog
Simon Willison's Weblog
博客园 - 【当耐特】
大猫的无限游戏
大猫的无限游戏
腾讯CDC
Latest news
Latest news
Project Zero
Project Zero
V
Vulnerabilities – Threatpost
Y
Y Combinator Blog
S
SegmentFault 最新的问题
Schneier on Security
Schneier on Security
C
CERT Recently Published Vulnerability Notes
W
WeLiveSecurity
罗磊的独立博客
Stack Overflow Blog
Stack Overflow Blog
酷 壳 – CoolShell
酷 壳 – CoolShell
L
LINUX DO - 热门话题
N
News and Events Feed by Topic
PCI Perspectives
PCI Perspectives
C
Cisco Blogs
Application and Cybersecurity Blog
Application and Cybersecurity Blog
爱范儿
爱范儿
T
The Exploit Database - CXSecurity.com
The Last Watchdog
The Last Watchdog
人人都是产品经理
人人都是产品经理
GbyAI
GbyAI
Know Your Adversary
Know Your Adversary
U
Unit 42

Все публикации подряд на Хабре

Ловим музу за клавиатуру: как айтишнику стать автором Что умеет Midjourney в 2026? Мой немного грустный разбор этого шикарного инструмента Никто не любит писать тесты, но ИИ может исправить это IPv8 выглядит как мечта. Поэтому почти наверняка не взлетит Производители вернули в продажу материнки с DDR3. Что происходит? Управление агентом с телефона через Telegram теперь в KodaCode От координации к лидерству: как меняется роль руководителя разработки Я сделала родителям бизнес вместо пенсии: зарабатываем 70 тысяч, мама не даёт продать В три раза быстрее приемка товара и оптимизация трудозатрат на 73%: как «РСТ-Инвент» помог Gulliver Group ИИ-шечный мир победил? О влиянии искусственного интеллекта на игропром Кремль снижает давление на Телеграмм пока Европа строит интернет по паспорту Как CEO, CTO и CIO за 8 часов собрали ИИ-директора, который умеет держать позицию под давлением Как (не) потерять домен за выходные Вместо 8 разных VPS: как я организовал практику студентам на одном сервере Почему твой Open Source проект не замечают? R&D: искусство управления неопределенностью в разработке AI-дефляция: вакансий для разработчиков больше, а рост зарплат — худший за 15 лет Мы отдали управление роботами OpenClaw. Что из этого вышло Галактический ID: система идентификации для всех форм разумной жизни Шесть основ бизнес-анализа: начинаем с вопроса «Кто в игре?» Код-ревью, в котором дело не в коде Данные переехали. Команда — нет Системной подход к сдаче OSWE в 2025 Почему комната управления реактором покрашена в цвет морской пены 4 YAML-файла вместо PySpark: как аналитикам строить пайплайны без разработчиков LLM-агент для поиска свободных доменов: автоматизируем подбор Когда, зачем и как правильно начинать новую сессию в Claude Code? Как я заставил нейросеть писать макросы для FreeCAD Анатомия ИИ‑агента для подбора персонала. От тысячи резюме к топ‑10 за минуты Опыт разработчика как экономика внимания Автономность как точка невозврата: кто будет субъектом в цифровом будущем Обучение ИИ в «диких» условиях: как рутинные действия превращаются в датасеты Как измерить LLM для задач кибербеза: обзор открытых бенчмарков Где хранить код? Сравнение GitHub, GitLab и Bitbucket Математика объясняет, почему нормальное распределение встречается повсюду Почему ваш FinOps не работает: 12 тезисов от практиков Как подписать проектную документацию УКЭП с использованием бесплатных лицензий Pilot Адаптивное администрирование Sigla Vision Я грузил уран в бочки, а потом 20 лет строил ИТ в атомной отрасли Чем позвонить с Эвереста? История и обзор спутниковой связи. Часть 2 Как языковая модель помогает контролировать качество инструктажей по охране труда в металлургии Как не передать на desktop свой IP в РКН Анатомия SAP Privileges: как устроено управление правами в macOS MoneyDev: Сказка про три главных слова Обновлённый токенизатор видео K-VAE 2.0 от Сбера Как сделать диспетчеризацию дома на 1284 квартиры почти бесплатно Как мы разогнали железную дорогу Мы дали агентам рутину. Теперь надо решить — что делать с освободившимся временем Токсичный контент, промпт-хакинг и защита ИИ — всё о Guardrails для LLM Умный город начинается с точного взгляда: как «Фалькон Тех» меняет пространство к лучшему Навайбкодил приложение для анализа графов Почему Дюну так интересно читать? Упрощаем работу с рутиной или как стать Гендальфом Белым Деконструкция Go: CPU, RAM и что там происходит. Go Assembler база. Часть 1.1 Какие профессии исчезнут из-за ИИ, а какие появятся? И что с этим делать Как мы построили IT-отдел, где хочется расти: архитектурные встречи, прозрачные метрики и книжные подарки Rufler: Делаем из Claude Code автономный рой через один YAML-конфиг Sing-box и белый список приложений Как построить надёжный обмен сообщениями в микросервисах: лучшие практики для enterprise OpenAI строит MLM-пирамиду, а McKinsey и Accenture помогают ей в этом Дом, который не построил Фишер (Часть 2) «Сверхзвуковой математик» против «Вдумчивого логиста»: битва алгоритмов 3D-упаковки Мультимодальные модели – грубый и дорогой инструмент Разговоры ничего не стоят. Код тоже Проверки физических лиц: с кого начнет ФНС Топ-10 бесплатных нейросетей для создания видео в 2026 году Первые слои кода: как наши решения сегодня определяют архитектуру ИИ на десятилетия Разработка нового статического анализатора: PVS-Studio JavaScript Поиск уязвимостей ПО: базовый минимум или роскошный максимум Почему оценка персонала не работает как инструмент управления Как мы разработали ИИ-ассистента и сократили рутину продуктовой команды на 50% Как я ушел из найма, нажарил косточек и продал на маркетплейсах на 168 млн в год Когда 1С:ERP уже внедрена, а нормального производственного плана всё ещё нет Как я сделал Claude мультимодальным, подключив к нему Qwen Omni Как приглашение на вакансию мечты превращается в атаку Infrastructure as Code: философия и лучшие практики IaC Тестируем Yandex Code Assistant на задаче, в которой нужно хранить секреты nxs-universal-chart v3.0: новое поколение универсального Helm-чарта Callback Injection: Техника, которая отправила Microsoft Defender в глухой нокаут «Все идеи на стол»: митап как способ вывести проект из тупика Сегодня я узнал нечто новое о GPU благодаря багу в своей игре Как заставить LLM ̶ ̶г̶а̶л̶л̶ю̶ ̶ эволюционировать Карта событий как фундамент аналитики: практический кейс для E-commerce Что выбрать для AI: x86, ARM или RISC-V? Дайджест железа за март Роль соматических мутаций в развитии аутоиммунных заболеваний: путь к избирательной терапии Mythos от Anthropic — тревожный сигнал для всех, а не только для банков Guardrails для LLM на Java: как приручить промпт‑инъекции и токсичные ответы Green-VLA: как мы собрали VLA-модель для реального антропоморфного робота и не потеряли обобщение Финансовая гонка вооружений: почему умные люди добровольно в ней участвуют Эра ИИ-агентов наступила: выбираем лучшего цифрового сотрудника # Практический опыт внедрения WinCC Redundancy на производственном предприятии Сделал MVP за 3 дня, а потом неделю прикручивал оплату. Оно того стоило? Физика против Маска: почему Starship V3 может оказаться ещё одной катастрофой Нефть Венесуэлы: крупнейшие запасы в мире, но не крупнейшая нефтяная держава JPA 4. Переосмысление Hibernate Почему зеркальная фотокамера Nikon D5 десятилетней давности идеально подошла для миссии «Артемида-2» Проект «Уровень-Спутник» или как мы сделали платформу для гидрологов «Замедлиться, чтобы ускориться»: почему ИИ повышает цену ошибок в требованиях и архитектуре Как с нуля поднять трафик IT-компании на 1657% при бюджете 55 тыс. и выжить Pixel-perfect Downsampling — идеальная отрисовка 50 миллионов точек без потерь
Фреймворк GameFramework.Pro (.Net)
Denis535 · 2026-05-17 · via Все публикации подряд на Хабре

Уровень сложностиПростой

Время на прочтение7 мин

Охват и читатели162

Туториал

В этой статье я хочу представить вам мой фреймворк, реализующий идеи чистой архитектуры адаптированные для игровых проектов. Данный фреймворк определяет основные слои вашего проекта, сущности и сервисы, а так же содержит минимальный набор утилит.

Обзор

Для начала давайте рассмотрим из чего состоит этот фреймворк.

System

  • Check - набор проверок для аргументов и операций.

  • DisposableBase - освобождаемый объект. Содержит некоторый дополнительный функционал.

  • IDependencyProvider - локатор служб. Предоставляет запрашиваемые объекты и значения.

GameFramework.Pro

  • Main - корень проекта.

    • ProgramBase - главная сущность проекта. Создает другие сущности и предоставляет запрашиваемые зависимости.

  • UI - аудио-графический пользовательский интерфейс.

    • ThemeBase - сущность аудио темы. Проигрывает музыкальные плейлисты.

    • PlayListBase

    • ScreenBase - сущность графического экрана. Показывает дерево виджетов.

    • WidgetBase

    • ViewableWidgetBase

    • RouterBase - сервис менеджера состояния. Предоставляет методы для загрузки главного меню, загрузки/перезагрузки/выгрузки игры, а так же выхода из приложения.

  • App - модель приложения.

    • ApplicationBase - сущность приложения. Выполняет инициализацию, запуск главного цикла, запуск игры, а так же предоставляет хранилище и подобное.

  • Game - модель домена / бизнеса (в нашем случае самой игры).

    • GameBase - сущность игры. Содержит информацию об игре, игровые правила, состояние и сущности игроков.

    • PlayerBase - сущность игрока. Содержит информацию об игроке и состояние. Так же может обрабатывать ввод.

    • WorldBase - сущность мира. Содержит все остальные сущности.

    • EntityBase - любой значимый для игры объект.

Детальный обзор

Теперь давайте рассмотрим некоторые компоненты этого фреймворка более детально.

Program

// Программа содержит другие сущности и сервисы,
// а так же реализует паттерн локатор служб.
// Заметьте, что локатор служб считается анти-паттерном,
// но другие решения приводят к оверкодингу.
public abstract class ProgramBase : DisposableBase {
  
    public ProgramBase();
    private protected override void OnDisposeInternal();
  
}
// Каждая сущность и сервис разделены на две класса: базовый и расширенный.
// Все расширенные классы добавляют поддержку IDependencyProvider.
public abstract class ProgramBase2<TTheme, TScreen, TRouter, TApplication> : ProgramBase, IDependencyProvider
    where TTheme : ThemeBase
    where TScreen : ScreenBase
    where TRouter : RouterBase
    where TApplication : ApplicationBase {

    protected TTheme Theme { get; init; }
    protected TScreen Screen { get; init; }
    protected TRouter Router { get; init; }
    protected TApplication Application { get; init; }

    public ProgramBase2();
    private protected override void OnDisposeInternal();

    object? IDependencyProvider.GetValue(Type type, object? argument);

}

Theme

// Тема содержит плейлист,
// который проигрывает текущий список аудио треков.
// Заметьте, что я использовал машину состояний,
// которая позволляет вам легко управлять текущим плейлистом.
public abstract class ThemeBase : DisposableBase {

    protected StateMachine Machine { get; }

    public ThemeBase();
    private protected override void OnDisposeInternal();

}
// Заметьте, что вместо наследования,
// я использовал двухсторонюю связь между плейлистом и состоянием.
public abstract class PlayListBase {
    public sealed class State2 : State {

        public PlayListBase PlayList { get; }

        public State2(PlayListBase playList);
        protected override void OnDispose();

        protected override void OnActivate(object? argument);
        protected override void OnDeactivate(object? argument);

    }

    public State2 State { get; }

    public PlayListBase();
    protected internal abstract void OnDispose();
    private protected virtual void OnDisposeInternal();

    protected internal abstract void OnActivate(object? argument);
    protected internal abstract void OnDeactivate(object? argument);

}
public abstract class ThemeBase2<TRouter, TApplication> : ThemeBase
    where TRouter : RouterBase
    where TApplication : ApplicationBase {

    protected IDependencyProvider Provider { get; }
    protected TRouter Router { get; }
    protected TApplication Application { get; }

    public ThemeBase2();
    private protected override void OnDisposeInternal();

}
public abstract class PlayListBase2 : PlayListBase {

    protected IDependencyProvider Provider { get; }

    public PlayListBase2();
    private protected override void OnDisposeInternal();

}

Screen

// Экран содержит дерево виджетов,
// которые рисуют текущий пользовательский интерфейс.
// Заметьте, что я использовал машину дерева,
// которая позволляет вам легко управлять иерархией виджетов.
public abstract class ScreenBase : DisposableBase {

    protected TreeMachine Machine { get; }

    public ScreenBase();
    private protected override void OnDisposeInternal();

}
// Заметьте, что вместо наследования,
// я использовал двухсторонюю связь между виджетом и нодой.
public abstract class WidgetBase {
    public sealed class Node2 : Node {

        public WidgetBase Widget { get; }

        public Node2(WidgetBase widget);
        protected override void OnDispose();

        protected override void OnActivate(object? argument);
        protected override void OnDeactivate(object? argument);

        protected override void Sort(List<INode> children);

    }

    public Node2 Node { get; }

    public WidgetBase();
    protected internal abstract void OnDispose();
    private protected virtual void OnDisposeInternal();

    protected internal abstract void OnActivate(object? argument);
    protected internal abstract void OnDeactivate(object? argument);

    protected internal virtual void OnBeforeDescendantActivate(INode descendant, object? argument);
    protected internal virtual void OnAfterDescendantActivate(INode descendant, object? argument);
    protected internal virtual void OnBeforeDescendantDeactivate(INode descendant, object? argument);
    protected internal virtual void OnAfterDescendantDeactivate(INode descendant, object? argument);

    protected internal virtual void Sort(List<INode> children);

}
public abstract class ViewableWidgetBase : WidgetBase {

    public object View { get; protected init; }

    internal ViewableWidgetBase();
    private protected override void OnDisposeInternal();

}
public abstract class ViewableWidgetBase<TView> : ViewableWidgetBase
    where TView : notnull {

    protected new TView View { get; init; }

    public ViewableWidgetBase();
    private protected override void OnDisposeInternal();

}
public abstract class ScreenBase2<TRouter, TApplication> : ScreenBase
    where TRouter : RouterBase
    where TApplication : ApplicationBase {

    protected IDependencyProvider Provider { get; }
    protected TRouter Router { get; }
    protected TApplication Application { get; }

    public ScreenBase2();
    private protected override void OnDisposeInternal();

}
public abstract class WidgetBase2 : WidgetBase {

    protected IDependencyProvider Provider { get;}

    public WidgetBase2();
    private protected override void OnDisposeInternal();

}
public abstract class ViewableWidgetBase2<TView> : ViewableWidgetBase<TView>
    where TView : notnull {

    protected IDependencyProvider Provider { get;}

    public ViewableWidgetBase2();
    private protected override void OnDisposeInternal();

}

Router

// Роутер предоставляет ссылки на другие сущности.
public abstract class RouterBase : DisposableBase {
  
    public RouterBase();
    private protected override void OnDisposeInternal();
  
}
public abstract class RouterBase2<TTheme, TScreen, TApplication> : RouterBase
    where TTheme : ThemeBase
    where TScreen : ScreenBase
    where TApplication : ApplicationBase {

    protected IDependencyProvider Provider { get; }
    protected TTheme Theme { get; }
    protected TScreen Screen { get; }
    protected TApplication Application { get; }

    public RouterBase2();
    private protected override void OnDisposeInternal();

}

Application

// Приложение предоставляет лишь провайдер зависимостей.
// Остальные сущности являются такими же простыми,
// поэтому я не буду продолжать описывать их.
public abstract class ApplicationBase : DisposableBase {
  
    public ApplicationBase();
    private protected override void OnDisposeInternal();
  
}
public abstract class ApplicationBase2 : ApplicationBase {
  
    protected IDependencyProvider Provider { get; }
  
    public ApplicationBase2();
    private protected override void OnDisposeInternal();
  
}

Дополнение

Все фреймворки определяют глобальную архитектуру проекта. А дизайн отдельных компонентов проекта, в лучшем случае, определяется соглашениями о стиле. Но одних стилей - недостаточно. Чтобы грамотно проектировать классы, вы должны понимать какие бывают классы и из чего они состоят. К сожалению, я не смог найти хоть какую-то информацию на эту тему. А классифицировать такие мелочи оказалось крайне сложно. Но в итоге, я все-таки смог прийти к интересным идеям, о которых хочу далее рассказать.

Я могу предложить следующую классификацию классов:

  • Сущность - класс названный существительным, который, в идеале, только обрабатывает события и не предоставляет API.

  • Сервис - класс названный отглагольным существительным, который, в идеале, только предоставляет API для других сущностей.

  • Информация (Info, Desc) - структура, описывающая существующий объект или процесс. Или описывающая создание объекта или запуска процесса т.е. просто аргументы.

  • Утилита - набор полезных методов.

Так же я могу предложить следующие классификации:

  • Для свойств классов:

    • Property/Data

    • Property/Info - информация об объекте.

    • Property/Info/Question - вопрос к объекту.

    • Property/Info/Assertion - утверждение об объекте.

    • Property/Info/Directive - инструкция объекту.

    • Property/Reference/Object - ссылка на объект.

    • Property/Reference/Delegate - ссылка на логику.

    • Property/Reference/Event - ссылка на обработчики события.

  • Для методов классов:

    • Method/Constructor

    • Method/Destructor

    • Method/Command/Query - команда на получение данных.

    • Method/Command/Request - команда на выполнение работы.

    • Method/Handler - реакция на событие.

  • Для атрибутов классов:

    • Attribute/Info

    • Attribute/Info/Directive

Заключение

В заключении, я хочу отметить, что большую сложность фреймворка я выделил в две отдельные библиотеки StateMachine.Pro и TreeMachine.Pro. Эти библиотеки могли бы быть достойны отдельных статей, но в целом их суть ясна из их названий.

Ссылки