Использование GetType/instanceof в C# против альтернатив

Я столкнулся с проблемой в игре, которую я делаю в C#. Это простая игра на основе плитки, и проблема возникла для включения питания, которое я пытаюсь сделать:

скажем, у нас есть основные типы плитки, круги квадраты и алмазы, которые все подклассы плитки. Вместо того, чтобы круги совпадали только с кругами, я попытался извлечь поведение "соответствует" абстрактному методу плитки: canMatchWith(плитка t). Плитки также имеют два метода для добавления / удаления плиток, с которыми они могут совпадать.

Итак, скажем, у нас есть круг плитка в середине нашей игры, и у нас есть powerup, который говорит: "круг плитки могут совпадать с квадратными плитками этот поворот". Я бы прошел через все плитки круга и сказал circleTile.addCanMatchWith (тип (квадрат)). Внутренне у нас есть список canMatchWith.

затем позже я хочу сказать: "круги больше не могут совпадать с квадратами" и просто сказать circleTile.removeCanMatchWith(тип(квадрат)).

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

  1. перечислений... Каждая плитка может быть составлена с переменной типа Tiletype. Это будет инициализировано в конструкторе и установлено в Type.Квадрат за квадратом и так далее. Затем каждая плитка будет иметь список canMatchWith, и функциональность такая же, как и моя оригинальная реализация. Только в этом случае, все немного сложнее. Скажем, у меня есть подклассы circle, oval и elipse. Я хочу, чтобы овалы могли совпадать только с квадратами, но элипсы могут совпадать со всеми кругами, а не квадратами.

проблема здесь в избыточности, мое перечисление теперь будет иметь овал и ELIPSE, А класс Elipse будет иметь (CIRCLE, OVAL, Elipse TileTypes) как типы, которые он может соответствовать с. Это совершенно излишне, я хочу просто сказать "круг", который я мог бы с типами. Я полагаю, что плитки могут иметь tiletype baseType и tiletype actualType.

  1. некоторая форма композиции поведения. Забудьте подклассы плитки, просто дайте методы плитки и переменную экземпляра для списка. Затем, во время выполнения мы можем только сказать 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