#c# #polymorphism
#c# #полиморфизм
Вопрос:
Я создаю игру на C #. Каждый уровень состоит из нескольких плиток. Каждая плитка имеет определенный тип, например, травяной пол, деревянная стена и т.д.
В идеале я хотел бы иметь один базовый класс «Tile» и наследовать от него для создания классов для каждого типа tile. Я хотел бы, чтобы свойства плитки были каким-то статическим / const / etc членом подкласса, поскольку каждый тип плитки всегда будет иметь одни и те же свойства. Т.Е. я не хочу, чтобы у 100 плиток было свойство, все из которых имеют одинаковое значение, что кажется довольно неэффективным.
Проблема в том, что вы не можете этого сделать в C #. Есть ли какие-либо способы достичь того, чего я хочу?
Моей альтернативной идеей было разделить все это на деревья, в одном из которых был бы только класс «Tile», представляющий экземпляр, и другой «TileType», из которого я создаю экземпляр одного объекта для каждого типа и, возможно, получаю к ним доступ через какой-то «TileTypeCollection». Хотя это кажется странным, я бы предпочел сделать это первым способом.
Существуют ли какие-либо общие рекомендации при работе с подобной ситуацией?
Комментарии:
1. В OO-программировании целью расширения базового типа является изменение или добавление поведения, а не изменение свойств / атрибутов. В вашем примере требуется только один класс Tile — конечно, могут быть разные типы tile, но это всего лишь атрибуты Tile. Допустим, ваш тип плитки имеет виртуальный метод PlayerEntered() , у вас может быть производный класс плитки с именем BlockingTile , который переопределит PlayerEnetered() и остановит проигрыватель, или у вас может быть класс BurningTile , который переопределяет PlayerEntered() и запускает проигрыватель..
2. Дело в том, что плитки имеют определенные свойства, такие как название их текстуры. Это значение всегда будет иметь одно и то же значение для определенного типа. Поэтому очень неприятно создавать экземпляры 100 плиток одного и того же типа, создавая 100 атрибутов, содержащих одну и ту же строку, которая никогда не изменится.
3. На слегка касательном примечании, если у вас есть 100 фрагментов, для каждого из которых установлено свойство с постоянной строкой «grass», компилятор будет иметь все ссылки, указывающие на один экземпляр, вместо создания 100 экземпляров String. Это означает, что вы используете только 4 (или 8) байта на плитку для ссылки, так что это далеко не так плохо, как вы думаете.
4. Это именно то, для чего разработан шаблон flyweight, как предлагает Марино Симич 🙂 Действительно ли каждому тайлу нужно строковое свойство для имени текстуры? Это то, что делает тайл уникальным? Нет… Я думаю, что тайлу в этом случае нужны только два свойства: позиция в игровом мире и идентификатор типа тайла… именно там будут использоваться свойства типа плитки (т. Е. текстуры).
Ответ №1:
Вы ищете шаблон проектирования с минимальным весом:
http://en.wikipedia.org/wiki/Flyweight_pattern
Flyweight — это шаблон проектирования программного обеспечения. Избыточный вес — это объект, который минимизирует использование памяти за счет совместного использования как можно большего количества данных с другими подобными объектами; это способ использования объектов в большом количестве, когда простое повторяющееся представление потребовало бы неприемлемый объем памяти.
Здесь есть несколько примеров C #:http://www.dofactory.com/Patterns/PatternFlyweight.aspx#_self2
Комментарии:
1. Это кажется лучшим решением. Большое спасибо.
2. Кроме того, возможно, у вас есть какой-либо опыт работы с XNA Framework? Если да, можете ли вы сказать, что импортер контента использует этот шаблон?
Ответ №2:
Вы можете создать структуру класса следующим образом:
public abstract class Tile
{
public abstract string BaseType;
}
public class Floor : Tile
{
public override string BaseType
{
get
{
return "floor";
}
}
}
public class Grass : Tile
{
public override string BaseType
{
get
{
return "grass";
}
}
}
public class Wooden : Tile
{
public override string BaseType
{
get
{
return "wooden";
}
}
}
Комментарии:
1. Это полное злоупотребление полиморфизмом. Смысл расширения типа заключается в добавлении / изменении поведения , а не в предоставлении разных данных .
2. @MattDavey: Спасибо, что указали на это. На самом деле я хотел прокомментировать это решение, поскольку я изучал C , и недавно, когда я хотел добиться того же в C #, я сделал это вот так. Можете ли вы указать мне на решение, которое вам нравится из приведенных здесь. Итак, я бы знал, как думать об этом в C #.
3. по сути, в OO-программировании объект состоит из двух элементов — атрибутов (properties) и поведения (methods). Вы можете изменять атрибуты объекта в любое время, но для добавления или изменения поведения требуется полиморфизм (не выдерживающий других шаблонов, например. Декоратор). Если вы видите мой комментарий в исходном вопросе, я привел пример того, когда полиморфизм может быть необходим 🙂
Ответ №3:
Разве вы не можете просто использовать статическое поле для резервного копирования свойства в вашем базовом классе Tile?
public abstract class Tile
{
private static string _commonProperty
public static string CommonProperty
{
get { return _commonProperty; }
set { _commonProperty = value; }
}
}
Комментарии:
1. Тогда свойство также должно быть статическим.
2. Не обязательно — может быть полезно иметь свойство, которое возвращает общее значение. (Когда вы реализуете интерфейс, имейте свойство с значением по умолчанию, но переопределяемым, наследуемое от класса или подготавливающее этот класс к наследованию …)
3. @Jean Hominal: но это свойство будет возвращать значение, общее для всех дочерних классов этого класса, и, насколько я понимаю, вопрос хочет возвращать разное его значение для каждого дочернего класса.
Ответ №4:
Одним из способов было бы иметь статические свойства в дочерних классах и нестатический виртуальный инструмент доступа в базовом классе, который при переопределении возвращает статические значения.
Ответ №5:
Есть несколько вариантов, которые приходят на ум в зависимости от конкретных требований.
ЕСЛИ существует общее свойство, значение которого отличается для каждого типа, то объявите его в базовом классе, сделайте его значением только для чтения и задайте его в конструкторе каждого типа.
Если у вас есть свойства, которые существуют только для определенного типа, то они должны быть объявлены в подклассе и аналогичным образом доступны только для чтения.