Snarky версия: Поздравляем, вы заново изобрели переменную.Плохо.Или, в лучшем случае, свойство интерфейса.
Полезная версия:
Я вижу несколько проблем с вашим дизайном.
Первыйпроблема в том, что это сложный .Осложнения лучше избегать, если у вас нет убедительной и существующей причины для этого (то есть: нет необходимости «возможно в будущем»).В противном случае ЯГНИ .Вы должны всегда пытаться выразить понятия непосредственно в коде, прежде чем прибегать к созданию систем для выражения этих понятий в данных (например, то, что я сказал о переизобретении переменной; также рассмотрим this ).
Но давайтепредположим, что у вас есть веская причина для разработки на основе компонентов ...
Второй вопрос - boxing .Упаковка происходит везде, где вы сохраняете тип значения (например: int
, float
, Vector2
, любой struct
) непосредственно в качестве ссылочного типа (например: object
, IEquatable
).Объекты в штучной упаковке являются неизменными - поэтому каждый раз, когда ваша позиция меняется, создается новый объект в штучной упаковке.Бокс переменной является (относительно) медленным.Объекты в штучной упаковке хранятся в куче - поэтому они учитываются во время и могут вызывать сборку мусора.Таким образом, дизайн, который вы предлагаете в своем вопросе, будет выполнять ужасно .
Я предполагаю, что ваша идея для дизайна на основе компонентов похожа на идею, описанную в этой статье ,Вот полезная диаграмма:
http://cowboyprogramming.com/images/eyh/Fig-2.gif
И это подводит меня к третьей проблеме: у вас не должно быть более одного компонента, который содержитпозиция в любом случае!(Похоже, в вашем дизайне вы собираетесь на более более гранулированный, чем вам нужно.)
По сути, компонентный дизайн - это переизобретение class
, а не переменная .В нормальном дизайне вы можете использовать функцию «Визуализация», например:
public void Draw()
{
spriteBatch.Draw(texture, this.Position, Color.White);
}
Но в компонентном дизайне вы будете иметь Draw
и Position
в разные классы .Который, между прочим, у меня был бы реализовывать интерфейсы как:
interface IRenderComponent { void Draw(); }
interface IPositionComponent { Vector2 Position { get; set; } }
Так как же Draw
доступ к Position
?Ну, вам нужен способ выразить this
(если вы собираетесь заново изобретать классы, этот , возможно, является наиболее важной концепцией, которую вам нужно включить).
Как бы вы это сделали?Вот примерная идея для вас:
Я бы сделал так, чтобы каждый класс компонента наследовался от Component
класса со свойством Self
.Я бы заставил Self
вернуть какой-то ComposedObject
с механизмом доступа к любому из других компонентов, которые составляют составной объект по интерфейсу.Поэтому, возможно, ваш компонент рендеринга может выглядеть следующим образом:
class SimpleRenderer : Component, IRenderComponent
{
public void Draw()
{
sb.Draw(texture, Self.Get<IPositionComponent>().Position, Color.White);
}
}
(это работает аналогично GameServiceContainer
(то есть: свойство Game.Services
). Идея в том, что ни у ComposedObject
не должно быть более одногоэкземпляр каждого интерфейса. Если количество интерфейсов невелико, ComposedObject
даже не нужно использовать список - просто сохраняйте каждый напрямую. Однако у вас могут быть компоненты, которые реализуют более одного интерфейса.)
Теперь, еслиэто слишком многословно для вас, возможно, вы могли бы добавить несколько удобных свойств в ComposedObject
(или использовать методы расширения) для общих фрагментов данных, например Position
, например:
public Vector2 Position { get { return Get<IPositionComponent>().Position; } }
Тогда вашФункция рисования может быть просто так:
spriteBatch.Draw(texture, Self.Position, Color.White);