Плохая модель: пример из видео
Один подписчик предоставил видео с кодом, демонстрирующим типичную проблему. В игре моделируется предметная область: рабочие собирают пшеницу, воины их защищают. Автор использовал плоскую модель – простые классы, подобные структурам данных (Data Transfer Objects), с публичными полями (например, класс Character с полями health, damage, speed). Это плохая практика, приводящая к архитектурным проблемам.
Несмотря на попытки улучшения кода (удаление лишних методов, добавление префикса _ к приватным полям), основная проблема осталась. Классы, такие как ResourceData (для ресурсов: уголь, пшеница), используют методы increase и decrease для изменения значений.
> Присутствие приставок Data или Info в именах классов – признак плоской модели, допускающей произвольное изменение данных без четких правил.
Проблема плоской модели и ScriptableObjects
Автор пытается использовать ScriptableObjects для хранения данных, но неправильно. Он создает экземпляры ScriptableObjects с публичными полями, сводя их к обычным классам данных. Это не использует преимуществ ScriptableObjects как persistent объектов, хранящихся в проекте и доступных в любое время. Более того, методы increase и decrease не решают проблему, поскольку позволяют произвольное изменение данных без контроля.
> Запрет изменения полей через публичные поля путем создания приватных полей и методов increase/decrease не решает проблему. Это лишь иллюзия безопасности, не ограничивающая изменение данных.
Правильный подход: богатая модель
Вместо плоской модели следует использовать богатую модель. Рассмотрим класс Wallet (кошелек). Вместо простого увеличения/уменьшения количества монет (coins), метод AddCoins включает проверку на максимальное количество монет и отправку события об изменении. Метод RemoveCoins проверяет наличие достаточного количества монет перед удалением, предотвращая ошибки.
> Богатая модель – композиция классов с хорошо определенными высокоуровневыми методами, encapsulate правила изменения данных. Простые правила (например, проверка наличия монет) содержатся в этих методах.
Это позволяет соблюдать концептуальные границы сущностей, избегая неконтролируемого изменения данных. Сложные операции разбиваются на более мелкие, с четко определенными правилами.
При работе со ScriptableObjects избегайте плоской модели данных. Создавайте богатые модели с четко определенными методами, обеспечивающими контроль над изменением данных и соблюдение концептуальных границ сущностей. Разбивайте сложные операции на более простые для упрощения разработки и отладки. Не используйте ScriptableObject как простую замену обычных классов данных. Используйте их преимущества как persistent объектов для хранения данных проекта, проектируя взаимодействие с ними через well-defined API.