Как связать две иерархии классов?

Наверное, выраженный одним предложением, вопрос звучит дико. Попробую сформулировать понятнее.
Игра разделена на некое подобие модели и представления. Имеется базовый класс для всех материальных объектов в игре: SpaceObject, у него есть наследники: Planet, Star, Ship, Asteroid и т.д.. Пока оно все "живет" внутри этой самой модели - все хорошо. Но как только возникает необходимость отобразить объект - появляются проблемы.
Итак, в случае, если объект появляется в одной локации с игроком (есть соответствующее событие), необходимо создать его "alter ego" в виде GameObject игрового движка. Проблема в таком коде:
void InitializeSpaceObject(SpaceObject spaceObject)
		{
			if (spaceObject is Star)
				InitializeStar(spaceObject as Star);
			else if (spaceObject is Planet)
				InitializePlanet(spaceObject as Planet);
			else if (spaceObject is AsteroidField)
				InitializeAsteroidField(spaceObject as AsteroidField);
			else
				throw new UnknownSpaceObjectTypeException("Cannot initialize SpaceObject of type " + spaceObject.GetType().ToString() + ": unknown type.");
		}

Понятное дело, такой код, как минимум, нарушает принцип подстановки Лисков, а вообще - просто неудобен в использовании и расширении. Но просто добавить ссылку на некий инициализатор объекта в классе SpaceObject нельзя - модель не должна ничего знать о движке и всей логики визуализации.
Как можно избавиться от этой вереницы if'ов или, по крайней мере, автоматизировать процесс поиска инициализатора для нужного типа объекта?

Ниже попробую изложить пару вариантов, которые мне пришли в голову - возможно они жизнеспособны?
1)Объявить таблицу "Тип" - "Инициализатор". Чему быть инициализатором - делегатом метода или полноценным классом, вопрос уже вторичный. Немного лучше, чем if'ы, но все равно как-то громоздко
2)Unity-стайл. Создать по обычному MonoBehaviour скрипту на каждый тип объекта. Каждый слушает событие появления объекта в локации. Внутри подписанного метода - одна проверка if (spaceObject is ThisScriptHandlingType) {код инициализации}. Что скажете про такой вариант?
  • Вопрос задан
  • 433 просмотра
Решения вопроса 1
Nipheris
@Nipheris Куратор тега C#
Если вы обязаны жить с разделением архитектурных слоёв, о котором вы упомянули и не можете ничего поменять: первый вариант "тип" - "инициализатор" вижу наиболее удобным. Все равно придется добавлять маппинг при добавлении нового типа объекта.
Вообще можно конечно продолжить извращение и воспользоваться рефлексией и сделать автомаппинг классов "модели" на наследников GameObject. Например, искать все классы "модели" в определенном неймспейсе и искать такие же в другом (которые, в свою очередь, будут наследниками от GameObject).

Вообще, вы в каком-то смысле идете вразрез с архитектурой самого движка, т.к. GameObject-ы - это не только "представление", а и модель тоже. Я понимаю, что вы наверное не хотите создавать тяжелые GameObject-ы, когда они вроде как не нужны еще (т.е. не в области видимости), но возможно, если не добавлять их в сцену, они не будут нагружать движок.
В общем, у меня есть сомнения в том, что такое разделение на модель и представление при использовании Unity - хорошая практика.
Ответ написан
Пригласить эксперта
Ответы на вопрос 1
byte916
@byte916
Почему бы в родительский класс SpaceObject не добавить абстрактный метод Initialize, который переопределить в каждом дочернем классе, и тогда ваш код сократится до
void InitializeSpaceObject(SpaceObject spaceObject)
    {
spaceObject.Initialize();
    }

А еще, говорят, лучше передавать в параметрах интерфейсы, то есть должно быть так
void InitializeSpaceObject(ISpaceObject spaceObject)
    {
spaceObject.Initialize();
    }
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы