ComplexModelSystem
Базовый класс для составных моделей данных, чья структура определяется подключёнными пакетами.
Назначение
Автоматическая сборка модели из всех конкретных реализаций интерфейса/базового класса T, найденных в загруженных сборках. Сериализация и десериализация через SerializeController.
- Сканирование
AppDomain.CurrentDomain.GetAssemblies()на реализацииT - Индексация экземпляров по типу:
Dictionary<Type, T> - Доступ к компонентам по типу:
Get<TU>() - Кэширование найденных типов (по
T) — повторныеInit()не сканируют сборки - Сериализация/десериализация через
SerializeController(property-based JSON)
Вне ответственности: создание экземпляров с параметрами конструктора, регистрация в Database, Unity-специфичная логика.
Зависимости
Vortex.Core.Extensions.LogicExtensions.SerializationSystem—SerializeProperties(),DeserializeProperties<T>()Vortex.Core.LoggerSystem— логирование ошибок
Архитектура
ComplexModel<T> (abstract, Serializable)
├── Cache — static Dictionary<Type, Type[]> (кэш типов по T)
├── Index — Dictionary<Type, T> (экземпляры компонентов)
├── Init() — сканирование сборок или восстановление из кэша
├── Get<TU>() — доступ к компоненту по типу
├── Serialize() — → JSON (вызывает BeforeSerialization/AfterSerialization)
├── Deserialize() — ← JSON (вызывает BeforeDeserialization/AfterDeserialization)
└── abstract hooks — BeforeSerialization, AfterSerialization, BeforeDeserialization, AfterDeserialization
Init()
- Очистка
Index - Если
Cacheсодержит типы дляT— создание экземпляров из кэша (Activator.CreateInstance) - Иначе — сканирование всех сборок: поиск не-abstract, не-interface типов, assignable от
T, с безпараметрическим конструктором - Сохранение найденных типов в
Cache[typeof(T)]
Требования к T
- Конкретные реализации должны иметь безпараметрический конструктор
- Не abstract, не interface
classconstraint
Контракт
Вход
- Вызов
Init()для сканирования и создания экземпляров Deserialize(string)для восстановления из JSON
Выход
Get<TU>()— типизированный доступ к компонентуSerialize()— JSON-строка черезSerializeController
Гарантии
- Кэш типов — повторные
Init()не сканируют сборки (для одногоT) Get<TU>()при отсутствии типа —null+ логErrorDeserialize(null/empty)— логError, индекс не изменяется- Исключения при сканировании сборок перехватываются — лог
Warning
Ограничения
- Один экземпляр на тип — дубликаты типов невозможны
Activator.CreateInstance— только безпараметрические конструкторы- Кэш статический по
T— общий для всех экземпляровComplexModel<T> - Сериализация через
SerializeController(experimental)
Использование
Определение модели
public interface IPlayerData { }
public class HealthData : IPlayerData
{
public int Hp { get; set; } = 100;
}
public class InventoryData : IPlayerData
{
public List<string> Items { get; set; } = new();
}
public class PlayerModel : ComplexModel<IPlayerData>
{
protected override void BeforeSerialization() { }
protected override void AfterSerialization() { }
protected override void BeforeDeserialization() { }
protected override void AfterDeserialization() { }
}
Инициализация и доступ
var model = new PlayerModel();
model.Init(); // найдёт HealthData, InventoryData
var health = model.Get<HealthData>();
health.Hp -= 10;
var inventory = model.Get<InventoryData>();
inventory.Items.Add("sword");
Сериализация
string json = model.Serialize();
var restored = new PlayerModel();
restored.Init();
restored.Deserialize(json);
Граничные случаи
| Ситуация | Поведение |
|---|---|
Get<TU>() с незарегистрированным типом |
null + лог Error |
Init() без реализаций T |
Пустой Index, работает без ошибок |
Повторный Init() |
Index очищается и пересоздаётся из кэша |
Сборка выбрасывает исключение при GetTypes() |
Перехват, лог Warning, продолжение сканирования |
Deserialize("") / Deserialize(null) |
Лог Error, Index не изменяется |
Два разных ComplexModel<T> с одним T |
Общий Cache, независимые Index |