Использование GetType/instanceof в C# против альтернатив
Я столкнулся с проблемой в игре, которую я делаю в C#. Это простая игра на основе плитки, и проблема возникла для включения питания, которое я пытаюсь сделать:
скажем, у нас есть основные типы плитки, круги квадраты и алмазы, которые все подклассы плитки. Вместо того, чтобы круги совпадали только с кругами, я попытался извлечь поведение "соответствует" абстрактному методу плитки: canMatchWith(плитка t). Плитки также имеют два метода для добавления / удаления плиток, с которыми они могут совпадать.
Итак, скажем, у нас есть круг плитка в середине нашей игры, и у нас есть powerup, который говорит: "круг плитки могут совпадать с квадратными плитками этот поворот". Я бы прошел через все плитки круга и сказал circleTile.addCanMatchWith (тип (квадрат)). Внутренне у нас есть список canMatchWith.
затем позже я хочу сказать: "круги больше не могут совпадать с квадратами" и просто сказать circleTile.removeCanMatchWith(тип(квадрат)).
Это мое текущее решение, и это отлично работает без недостатков производительности, которые я заметил (это игра на основе плитки, поэтому эти типы оцениваются только один раз за "ход", а не кадр за кадром). Однако голос в моей голове говорит мне, что это плохой способ добиться такого поведения. Поэтому у меня есть несколько вариантов:
- перечислений... Каждая плитка может быть составлена с переменной типа Tiletype. Это будет инициализировано в конструкторе и установлено в Type.Квадрат за квадратом и так далее. Затем каждая плитка будет иметь список canMatchWith, и функциональность такая же, как и моя оригинальная реализация. Только в этом случае, все немного сложнее. Скажем, у меня есть подклассы circle, oval и elipse. Я хочу, чтобы овалы могли совпадать только с квадратами, но элипсы могут совпадать со всеми кругами, а не квадратами.
проблема здесь в избыточности, мое перечисление теперь будет иметь овал и ELIPSE, А класс Elipse будет иметь (CIRCLE, OVAL, Elipse TileTypes) как типы, которые он может соответствовать с. Это совершенно излишне, я хочу просто сказать "круг", который я мог бы с типами. Я полагаю, что плитки могут иметь tiletype baseType и tiletype actualType.
- некоторая форма композиции поведения. Забудьте подклассы плитки, просто дайте методы плитки и переменную экземпляра для списка. Затем, во время выполнения мы можем только сказать someTile.addCanMatch (new CircleMatchBehavior ()). Это кажется глупым, так как у меня будет куча классов, просто говорящих, что вы можете соответствовать определенному форма.
В общем, то, что я пытаюсь выполнить, - это иметь несколько типов объектов, способных взаимодействовать с любым количеством разных типов. Вопрос в том, что я должен использовать для типа. Можно ли использовать GetType здесь? Перечисления? Или есть лучшая стратегия, которую кто-то порекомендовал бы? Я пытаюсь быть как можно более общим, эти плитки не должны иметь каких-либо жестко закодированных зависимостей от других плиток и должны иметь возможность изменять, с кем они могут взаимодействовать на лету. Скажи Мне ... сделайте новый подкласс плитки, Пентагон... Ну, пятиугольники могут совпадать с квадратами, кругами и пятиугольниками. Легко с моей реализацией, но что-то говорит мне, что это грязная практика ООП.
Я чувствую, что должен использовать типы/перечисления, потому что я не пытаюсь сказать thisTile.addCanMatch (плитка someOtherObject). Это слишком специфично, я хочу, чтобы thisTile мог соответствовать всем плиткам, которые являются экземплярами определенного класса.
2 ответов
Если все формы аналогичного типа всегда будут разделять поведение, то имеет смысл не хранить это поведение на уровне "на экземпляр". Вместо этого у вас может быть "CanMatchManager", который хранит словарь списков, индексированных по типу фигуры. Затем, когда круг пытается сравнить совпадение, он запрашивает типы, которые могут совпадать с MatchManager. Кроме того, MatchManager может принимать две фигуры и определять, соответствуют ли они. Это Шаблон Медиатора
Я знаю, что на вопрос уже ответили и приняли, но я сделал что-то подобное один раз, и я думал, что просто опубликую код здесь.
public class TypeMatchManager
{
private Dictionary<Type, List<Type>> savedMatches = new Dictionary<Type, List<Type>>();
public TypeMatchManager() { }
public void AddMatch(Type firstType, Type secondType)
{
this.addToList(firstType, secondType);
this.addToList(secondType, firstType);
}
public void DeleteMatch(Type firstType, Type secondType)
{
this.deleteFromList(firstType, secondType);
this.deleteFromList(secondType, firstType);
}
public bool CanMatch(Type firstType, Type secondType)
{
List<Type> firstTypeList = this.findListForType(firstType);
List<Type> secondTypeList = this.findListForType(secondType);
return (firstTypeList.Contains(secondType) || secondTypeList.Contains(firstType));
}
private void addToList(Type firstType, Type secondType)
{
var matchingTypes = this.findListForType(firstType);
if (!matchingTypes.Contains(secondType))
{
matchingTypes.Add(secondType);
}
}
private void deleteFromList(Type firstType, Type secondType)
{
var matchingTypes = this.findListForType(firstType);
if (matchingTypes.Contains(secondType))
{
matchingTypes.Remove(secondType);
}
}
private List<Type> findListForType(Type type)
{
foreach (var keyValuePair in savedMatches)
{
if (keyValuePair.Key == type)
{
return keyValuePair.Value;
}
}
savedMatches.Add(type, new List<Type>());
return findListForType(type);
}
}
класс был разработан так, чтобы не имело значения, какой параметр вы указываете, какой тип; он проверяет type1.список имеет type2 и type2.список имеет тип.
простой пример:
typeManager.AddMatch(a, b);
Console.WriteLine(typeManager.CanMatch(a, b)); // True
typeManager.DeleteMatch(b, a);
Console.WriteLine(typeManager.CanMatch(a, b)); // False
Console.WriteLine(typeManager.CanMatch(a, c)); // False
typeManager.AddMatch(a, c);
Console.WriteLine(typeManager.CanMatch(a, c)); // True
Console.WriteLine(typeManager.CanMatch(a, d)); // False
typeManager.AddMatch(b, d);
Console.WriteLine(typeManager.CanMatch(a, d)); // False
Console.WriteLine(typeManager.CanMatch(d, b)); // True
typeManager.DeleteMatch(d, b);
Console.WriteLine(typeManager.CanMatch(d, b)); // False
Console.WriteLine(typeManager.CanMatch(b, d)); // False