Как создать экземпляр определенного подкласса в зависимости от идентификатора или есть лучший способ?

Предыстория:
В настоящее время я работаю над игрой, в которой будет несколько типов мебели, таких как стулья, столы или картины, с которыми игрок сможет взаимодействовать. Для этого я создал абстрактный класс GameObject, который обрабатывает все, что есть общего у всех этих объектов (например, рисование, хранение переменных положения и т. д.). Предметы мебели будут иметь несколько разных параметров, таких как ширина и высота (целые числа), а также то, что происходит, когда пользователь решает взаимодействовать с ними (например: стулья -> если игрок близко, садитесь, столы -> ничего). , картины -> рассматривать независимо от расстояния). Мебель можно создать из меню, которое возвращает какой-то идентификатор для выбранного предмета (в настоящее время целое число).

Вопрос:
Как реализовать разные предметы мебели?
Один из способов — создать подкласс для каждого предмета мебели. Но у этого есть два недостатка; Во-первых, будет сложно управлять таким количеством классов, а во-вторых, когда будет создана мебель, нужно будет каким-то образом создать правильный подкласс GameObject на основе идентификатора (см. дополнительный вопрос).

Мне кажется, что наиболее логичным вариантом является создание общего класса, который принимает параметры ширины и высоты в конструкторе, а также работающий/другой интерфейс, который может запускаться, когда пользователь щелкает объект. Различные значения могут быть сохранены в базе данных и извлечены на основе идентификатора, но проблема все еще возникает с различными действиями, поскольку Runnables не могут быть сохранены извне, и это приводит к той же проблеме, что и решение 1; Много разных классов, где нужно выбрать один для текущего объекта.

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

Игра предназначена для платформы Android, но в будущем может быть порт для IOS, поэтому ответы, специфичные для Java, не предпочтительны, но приемлемы.


person Jave    schedule 18.10.2012    source источник
comment
Чтобы создать один из нескольких классов на основе параметров, взгляните на шаблон проектирования factory.   -  person John3136    schedule 18.10.2012
comment
Почему было бы трудно управлять классом для каждого предмета мебели? Мне кажется, это хороший план.   -  person Keppil    schedule 18.10.2012
comment
Лучше иметь один класс для каждого объекта, чем скомкать все поведения в один большой класс. Я бы последовал совету Джона - шаблон проектирования фабрики   -  person Tomislav Novoselec    schedule 18.10.2012
comment
Фабричный шаблон, кажется, то, что мне нужно использовать. Я, вероятно, также напишу общую реализацию мебели, которую я смогу использовать для тех, которые идентичны, кроме внешнего вида.   -  person Jave    schedule 18.10.2012
comment
@Keppil: Если бы у меня было ›200 классов для различной мебели и я добавил бы абстрактный метод в суперкласс, мне пришлось бы написать ›200 новых реализаций этого метода.   -  person Jave    schedule 18.10.2012
comment
Этот комбинированный подход звучит лучше всего.   -  person John Watts    schedule 18.10.2012
comment
@Jave: Да. Я все еще не вижу проблемы.   -  person Keppil    schedule 18.10.2012


Ответы (3)


Действительно сложно придумать идеальное дизайнерское решение проблемы, не учитывая все "если" и "но".

Тем не менее, одной из важнейших частей вашей игры является взаимосвязь между предметами (мебель, картина и т. д.) и действиями, которые можно выполнять с каждым предметом. Вот как вы можете смоделировать эти отношения (используя композицию):

Действие : Интерфейс, представляющий игровое действие. Содержит единственный метод с именем PerformAction.

SittingAction : конкретный класс, реализующий действие. Представляет собой игровое действие для сидения. Реализует метод PerformAction для определения кода для выполнения сидячего действия.

ExamineAction : конкретный класс, реализующий действие. Представляет игровое действие по исследованию предмета. Реализует метод PerformAction для определения кода для выполнения действия проверки.

Точно так же вы можете определить одно действие для каждого типа действия, которое может быть выполнено в вашей игре.

GameItem : класс, представляющий предмет в игре. Содержит свойства, общие для всех элементов, такие как имя элемента, ширина, ширина, глубина и т. д., а также геттеры и сеттеры для этих свойств. GameItem имеет переменную экземпляра типа Action и конструктор, который принимает параметр Action для инициализации Action. GameItem также определяет метод onInteraction, который можно вызывать, когда пользователь взаимодействует с GameItem. Этот метод делегирует задачу выполнения действия, связанного с GameItem, путем вызова метода PerformAction для переменной экземпляра Action. Следовательно, любой класс, создающий экземпляр GameItem, должен передать соответствующее действие конструктору GameItem, представляющему действие, связанное с GameItem. Когда пользователь взаимодействует с элементом, просто вызовите метод onIneraction.

GameController : класс, который прослушивает пользовательские события и вызывает метод onIneraction для GameItem, с которым взаимодействовали.

Вот и все. Чисто и просто. Нет необходимости в каких-либо операторах if else, чтобы проверить, какое действие выполнять над каким элементом. Simpy создает экземпляры всех ваших экземпляров GameItem с соответствующими экземплярами Action, а ваш GameController, который прослушивает пользовательский ввод, вызывает метод onIneraction для GameItem, с которым взаимодействовали.

person CKing    schedule 18.10.2012
comment
Спасибо за ваш вклад, это в значительной степени то, над чем я начал работать. - person Jave; 19.10.2012
comment
Рад узнать, что это было полезно. ;) У вас может быть список действий в GameItem, если с GameItem связано более одного действия. Просто мысль! - person CKing; 21.10.2012

Забудьте об универсальном классе; вы должны создавать подклассы, как вы представили в своем первом варианте. О ваших недостатках:

будет сложно справиться с таким количеством классов

Почему? Если вы создаете подклассы, вы должны сделать что-то вроде этого:

GameObject chair = new Chair();

и если вы будете следовать общему режиму, вы должны сделать что-то вроде этого:

GameObject chair = new GameObject(Type.CHAIR);

Создание обоих требует практически одинаковых усилий, но у вас гораздо больше преимуществ при использовании первого. Разница в том, что вы не можете обрабатывать различные действия со вторым подходом, не выполняя множество условных проверок. Кроме того, ваше ядро ​​будет сильно связано с вашими существующими объектами, а это означает, что вам нужно будет изменить ядро, если вы измените один из классов, и наоборот. С первым вариантом вы можете сделать что-то вроде этого:

selectedObject.makeAction();

и он вызовет правильное действие на основе сохраненного экземпляра. Если вы будете следовать общему подходу, это будет выглядеть так:

if(selectedObject.getType() == Type.CHAIR) {
    //do char action. Notice that this ins't even a method provided by GameObject, you will need to get this from another place, since the GameObject is generic.
}     if(selectedObject.getType() == Type.TABLE) {
     //do table action
} if....

при создании мебели нужно будет каким-то образом создать правильный подкласс GameObject на основе идентификатора

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

person Daniel Pereira    schedule 18.10.2012
comment
Спасибо за ответ, под сложным управлением я имел в виду не использование классов, а скорее их написание и/или обновление при необходимости. рассмотрим ›200 различных классов, которые необходимо написать/обновить. - person Jave; 18.10.2012
comment
Относительно you already know the object types, you just need to instanciate the correct class: Да, я просто не хотел писать переключатель из 200 пунктов. - person Jave; 18.10.2012
comment
Что вы можете сделать в этом случае, так это создать общие подклассы. Допустим, у вас есть стул, кушетка и скамья; действие для них - сидеть, но между ними что-то меняется (например, рисунок). Вы можете создать общий подкласс и реализовать на нем действие «сидеть», расширить этот подкласс и реализовать только метод рисования. Вы напишете действие «сидеть» только один раз, и у вас будут три объекта, выполняющие его, меняя только то, что вам действительно нужно. - person Daniel Pereira; 18.10.2012
comment
Насчет переключателя делать не надо. Вы можете создать файл, описывающий вашу сцену (что-то похожее на файл OBJ для описания графических объектов), а затем создать фабрику для обработки объектов. И помните: переключатель, который вам не нужно делать в экземпляре, потребуется, когда вы будете обрабатывать действия, если вы будете следовать подходу общего класса. - person Daniel Pereira; 18.10.2012
comment
Да, думаю, этим я и займусь. Я не очень понимаю, что вы имеете в виду под файлом, не могли бы вы опубликовать пример? - person Jave; 18.10.2012
comment
@Jave: Если вам нужно обновить 200 классов, чтобы изменить какое-то поведение, это поведение обязательно должно быть в суперклассе. - person Keppil; 18.10.2012
comment
@Jave Вам не нужно создавать сцену в коде, вместо этого вы можете загрузить ее из файла. Таким образом, вам нужно только вызвать метод loadFile, чтобы получить другую сцену. Вы можете определить этот файл по своему усмотрению. Вы можете сделать что-то вроде: объект: Идентификатор стула: myChair ширина: 100 высота: 100 позиция: [10, 20] - person Daniel Pereira; 18.10.2012
comment
Ах, я понимаю, что вы имеете в виду, но пользователь может размещать объекты в игре, поэтому их нужно будет создавать по мере того, как это происходит. :) - person Jave; 18.10.2012

Я бы определенно пошел с подклассами для различных типов мебели.

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

Что касается действий, которые можно выполнять с каждой частью, вы можете использовать интерфейсы:

Каждый класс мебели будет реализовывать набор интерфейсов в зависимости от их возможных действий:

Interface IExaminable {
  public <result> examine();
}

Interface IBuyable {
  public <result> buy();
}

а затем вы можете легко проверить, доступен ли объект для исследования с помощью оператора istanceof (при необходимости).

Надеюсь, поможет

person richardtz    schedule 18.10.2012