Предупреждение: этот вопрос использует аналогию с RPG в качестве примера.
Скажем, я делаю эту RPG, о которой я мечтал, используя С#. Когда игрок входит в битву, появляется какое-то поле битвы, которое содержит ссылки на все элементы, относящиеся к битве, такие как различные противники и предметы, доступные на поле боя.
Теперь все эти элементы имеют одну, но несколько ролей: например, Союзник (который является Истребителем через прямое наследование) способен двигаться на поле битвы, выдавать команды или быть целенаправленными с помощью ennemies.
Теперь, когда могучий меч в камне также имеет несколько ролей в битве. Очевидно, что он не может перемещать и выдавать команды, но он все равно может быть нацелен, и он может (надеюсь) быть снят или использован.
Все эти поведения представлены интерфейсами в моем коде, так что все объекты с одинаковыми поведением могут использоваться независимо от объекта, который его реализует.
Пример кода:
public class Ally : Fighter, IMovable, IActor, ITarget
{
// IMovable implementation
public void Move(...) { ... }
// IActor implementation
public bool HasInitiative { get { ... } }
public ICommand IssueCommand(...) { ... }
// ITarget implementation
public void OnTarget(...) { ... }
// other code ...
}
public class MightySword : ITarget, ILiftable, IUsable
{
// ITarget implementation
public void OnTarget(...) { ... }
// ... other interface implementation
// other code ...
}
Теперь мой вопрос следующий: Как должен мой класс Battlefield ссылаться на все эти объекты? Я придумал два возможных решения, но на данный момент неясно, какой из них лучше, и нужно ли найти лучшие решения.
Решение A:
Ссылка несколько раз, прямой доступ: Используя это решение, каждый объект ссылается один раз на интерфейс, который он реализует, поэтому мой класс боя будет выглядеть следующим образом:
public class Battlefield : ...
{
IList<IMovable> movables;
IList<ITarget> targets;
IList<IActor> actors;
// ... and so on
}
и метод Battlefield.Update может выглядеть следующим образом:
Update(...)
{
// Say every actor needs to target somebody else
// it has nothing to do in the Update method,
// but this is an example
foreach (IActor actor in actors)
{
if (actor.HasInitiative)
{
ITarget target = targets[actor.TargetIndex];
target.OnTarget(actor.IssueCommand(...));
}
}
}
Это решение позволяет нам иметь прямой доступ к различным поведением объектов, но он вводит избыточность, так как союзник должен быть привязан к движимым, целям и субъектам , Эта избыточность означает больший объем памяти и сделает добавление и удаление объекта на поле битвы намного сложнее (каждый тип должен теперь из каких коллекций он должен быть добавлен/удален).
Кроме того, добавление нового интерфейса означает добавление новой коллекции для хранения элемента и управления соответствующим кодом.
Решение B:
Ссылка один раз, фильтр доступа: Используя это решение, все объекты происходят из одного класса или интерфейса (что-то похожего на IBattleElement) и хранятся в одной коллекции. При использовании элементов они фильтруются в соответствии с их типами:
Update(...)
{
IList<IActor> actors = elements.OfType<Actor>();
foreach (IActor actor in actors)
{
ITarget target = elements.OfType<Target>()[actor.TargetIndex];
target.OnTarget(actor.IssueCommand(...));
}
}
Там. Больше нет избыточности, но мне не нравится использование конструкции "typeof", и я вообще стараюсь ее избегать. Очевидно, что это хуже, чем время выполнения.
Я реализовал решение A в своем игрушечном проекте, но в этом проекте нет ничего критически важного. Ответ должен учитывать не только производительность (как память, так и процессорную мудрую), но и то, что предлагает лучший дизайн для операций, которые мне или мои дизайнеры могут выполнять, например, добавление новых классов BattleElement или новых интерфейсов для новых действий, добавление и удаление экземпляров объектов из Battlefield и, в более общем плане, их использование в логике игры.
Прошу прощения за общую глупость моих примеров, я надеюсь, что мой английский достаточно хорош, и что мой вопрос несет хотя бы немного интереса и не указывает на общую глупость в самом дизайне.