Lyra Inventory Fix 04: Решаем проблему с валидацией, инкапсуляцией и имплементацией.
Фикс 1: Валидация, инкапсуляция
Нашли TODO, зарытый на 136-й строкеLyraInventoryManagerComponent.cpp.
"Would prefer to not deal with this here and hide it further?"
Перевод: "Не хочу возиться с этим здесь, спрячьте куда-нибудь подальше."
Суть проблемы: Валидация (nullptr, проверка состояния Entry.Instance) выполняется прямо в компоненте. Логика смешивается, инкапсуляция нарушается, код превращается в свалку проверок и условий.
Решение
Скрываем валидацию там, где её место — в подсистеме.
- Логика проверок уходит из компонента.
- Подсистема берёт на себя всю грязную работу.
- Компонент вызывает только готовые методы.
Что это даст?
- Чистота кода: компонент будет заниматься своим делом, не влезая в проверку данных.
- Надёжность: валидатор в подсистеме будет унифицирован и не допустит пропущенных проверок.
- Лёгкость поддержки: захочешь изменить валидацию? Меняй в одном месте — подсистема всё обработает.
В компоненте инвентаря не будет никакого мусора. Только вызовы, которые работают.
Спойлер: Для выполнения дальнейших телодвижений необходимо завершить предыдущие гайды:
В своих потугах будем использовать UE5.5.0.
Что мы делаем?
Мы берём грязную работу и отдаём её подсистеме, чтобы компонент наконец-то мог дышать спокойно, не забивая голову бесполезными проверками.
Переносим логику проверки валидности и очистки в ULyraInventorySubsystem
- Валидация: Проверка на nullptr и другие ошибки состояния больше не беспокоят компонент.
- Фильтрация: Подсистема фильтрует только валидные экземпляры.
- Очистка: Убираем невалидные предметы автоматически. Мусор не задерживается.
Обновляем FLyraInventoryList
Теперь компонент не несёт ответственность за эти проверки. Все они делегированы подсистеме. Компонент лишь запрашивает, а подсистема делает.
"Пусть каждый делает то, для чего он предназначен."
LyraInventorySubsystem.h:
LyraInventorySubsystem.cpp:
LyraInventoryManagerComponent.h:
Обновляем FLyraInventoryList
Теперь компонент инвентаря делегирует проверку валидности и очисткуподсистеме.
LyraInventoryManagerComponent.cpp:
Что мы сделали?
Добавили методы в подсистему
- IsValidItemInstance: Проверяет валидность экземпляра.
- GetValidItemsFromList: Оставляет только валидные предметы, весь мусор вон из списка.
- CleanupInvalidItems: Убирает всё, что не прошло проверку. Грязные предметы — не наша забота.
Перенесли логику валидации из компонента в подсистему:
- FLyraInventoryList теперь передаёт задачи подсистеме. Ни nullptr, ни другие тупые проверки не тревожат компонент.
Улучшили инкапсуляцию и масштабируемость:
- Вся логика валидации теперь под контролем ULyraInventorySubsystem. Любая новая логика — логирование, асинхронная проверка, кастомные ошибки — теперь не проблема.
Преимущества нового подхода
- Централизованная валидация: Никаких раскиданных проверок по всему коду. Вся грязная работа — в подсистеме.
- Чистый и модульный код: Компоненты теперь не врут про проверки. Они просто делают своё дело.
- Автоматическая очистка: Подсистема удаляет все невалидные предметы. Никаких ошибок, никаких утечек.
- Гибкость: Расширить систему — добавить новые проверки или асинхронное обновление? Пожалуйста. Всё есть.
Вывод
Мы не просто перенесли логику. Мы её закапываем там, где ей и место. Компонент инвентаря стал легче, а код — чище и надежнее.
Как говорится: "Меньше проверок руками — больше времени на крутые фичи!"
Фикс 2: Имплементация
Молодёжь, конечно… Какое-то постоянное состояние неопределённости. Как этот unimplemented(), который каждый раз шепчет: "Начну с понедельника." А потом ты такой — раз, и забываешь, а код остаётся как подгузник, который ты не сменил вовремя. Всё вроде работает, но разгоняется — и бах!
Два метода AddEntry — зачем они нужны?
ULyraInventoryItemInstance* AddEntry(TSubclassOf<ULyraInventoryItemDefinition> ItemClass, int32 StackCount)Это наш молодой и амбициозный метод. Он создаёт новый предмет с нуля:
- Берёт определение предмета (ItemClass).
- Создаёт экземпляр через подсистему.
- Устанавливает размер стека (потому что не всегда нужен одиночный батончик хлеба — иногда их надо 20).
- Используется при создании новых предметов: игрок нашёл лопату, квест наградил булкой, или админ выдал баночку зелья.
void AddEntry(ULyraInventoryItemInstance* Instance) А вот это старый волк в системе — работает с уже существующими экземплярамипредметов:
- Экземпляр уже есть — неважно, откуда он: репликация, загрузка сохранёнки или переложили из другого инвентаря.
- Нужна только проверка валидности (а то мало ли, пока предмет полз к нам, превратился в тыкву).
- Подсистема может его дополнительно инициализировать, если что-то не так.
Зачем вообще два метода?
Потому что жизнь никогда не бывает линейной. Всё сложнее.
Первый метод — для новых экземпляров. Мы создаём что-то с нуля, задаём параметры и отправляем в мир. Это как роддом для предметов.
Второй метод — для существующих предметов. Вроде как, пришёл, а тут тебе только нужно проверить, а не стал ли он тыквой по дороге. Если всё в порядке — подсистема его доделает, если надо.
Почему unimplemented() — это плохо?
Забыть про этот метод — как забыть прикрутить гайку. Всё будет работать, пока не разгонишься. А потом в самый неподходящий момент машина возьмёт и развалится на части. Так вот, этот метод — необходим. А если его нет, то весь процесс создаётся в голове как пустое место, как заброшенная деревня, куда никто не заходит.
Как его реализовать?
Не будем терять время на всякие отговорки. Заполняем пробел и двигаемся дальше:
LyraInventoryManagerComponent.cpp
Теперь всё на своих местах:
- AddEntry(ItemClass, StackCount) — создаёт новый предмет.
- AddEntry(Instance) — добавляет существующий экземпляр.
Вывод
Два метода — два подхода: создать или добавить. Оба играют свою роль. И если какой-то из них не был реализован, а ждет, как старик на утренней рыбалке — не откладывайте! Сразу займитесь этим, иначе потом будете искать баг, когда всё уже валится на куски. Лучше сразу доделать, чем потом искать баг, когда всё уже развалилось!