Замена вложенных операторов Switch - PullRequest
0 голосов
/ 30 августа 2018

Я пытаюсь сделать многофункциональный скрипт для кнопок, которые изменяют переменные внутри моего статического игрового контроллера. При нажатии каждая из них должна изменить правильную переменную, с которой они связаны сериализованными перечислениями. Идея заключается в том, что я могу дать им пощечину на любую кнопку и просто изменить то, что они делают на какие данные, изменив несколько флагов на экземпляре сценария кнопки.

Проблема заключается в количестве возможных перестановок, у меня есть две «Армии», каждая с четырьмя «Единицами», каждая из которых имеет пять «Атрибутов». Я получаю действительно густую кучу кода, который, я уверен, можно как-то решить, но не могу найти никакого решения.

(для ясности activeArmy, unitToChange, unitNum и attributeToChange все enums)

if (activeArmy == ArmyNum.army1)
{
    switch (attributeToChange)
    {
        case AttributeType.Accuracy:
            switch (unitToChange)
            {
                case UnitNum.unit1:
                    //if it's not too large
                    GameController.controller.army1.unit1.accuracy++;
                    //recalculate unit cost
                    GameController.controller.army1.unit1.RecalculateCost()
                    //then give the new number to the UI to display it
                    break;

Теперь я бы хотел иметь возможность сделать что-то вроде этого:

GameController.controller.[activeArmy].[unitToChange].[attributeToChange]++;

Возможен ли такой путь? Есть ли способ заменить в перечислениях пути к таким переменным?

Это то, что я решил бы, изменив мою архитектуру?

Ответы [ 2 ]

0 голосов
/ 30 августа 2018

Вот гибкая архитектура, которую вы можете попробовать. Я бы порекомендовал использовать строковые идентификаторы для ваших армий / юнитов / атрибутов вместо перечислений, потому что с помощью перечислений вы блокируете свой набор значений, и вы никогда не сможете иметь больше армий или юнитов, чем в вашем перечислении.

Если вы продолжаете использовать перечисления, задайте для свойства Army / Unit / Attribute objects 'Name значение ToString перечисления, чтобы этот код работал. Например, army.Name = ArmyTypes.Navy.ToString()

public class Army
{
    // The army's name
    public string Name { get; set; }

    // List of units in the army
    public List<Unit> Units { get; set; }

    // Get the named unit from this army
    public Unit GetUnit(string name)
    {
        foreach (var unit in this.Units)
        {
            // Case insensitive comparison
            if (string.Compare(unit.Name, name, true) == 0)
            {
                return unit;
            }
        }
        return null;
    }
}

public class Unit
{
    // The unit's name
    public string Name { get; set; }

    // The unit's attributes
    public List<UnitAttribute> Attributes { get; set; }

    // Get the named attribute from this army
    public UnitAttribute GetAttribute(string name)
    {
        foreach (var attribute in this.Attributes)
        {
            if (string.Compare(attribute.Name, name, true) == 0)
            {
                return attribute;
            }
        }
        return null;
    }
}

public class UnitAttribute
{
    // The attribute's name
    public string Name { get; set; }

    // Current attribute value and maximum attribute value
    public int CurrentValue { get; set; }
    public int MaxValue { get; set; }

    // Add a value to this attribute and don't let it go above its maximum
    public void Add(int value)
    {
        this.CurrentValue += value;
        if (this.CurrentValue > this.MaxValue)
        {
            this.CurrentValue = this.MaxValue;
        }
    }
}

public class Controller
{
    public List<Army> Armies { get; set; }

    // I'd recommend using string parameters for your buttons instead 
    // of enum parameters, but you can do it either way
    public void ModifyUnitButtonHandler(ArmyTypes army, UnitTypes unit, UnitAttributeTypes attribute, int value)
    {
        // I'm not sure if the ? operator exists in Unity's default .NET version.
        // If not, you can use a few if/then statements to make sure that nothing
        // returns null
        GetArmy(army.ToString())?
            .GetUnit(unit.ToString())?
            .GetAttribute(attribute.ToString())?
            .Add(value);
    }

    private Army GetArmy(string name)
    {
        foreach (var army in this.Armies)
        {
            if (string.Compare(name, army.Name, true) == 0)
            {
                return army;
            }                
        }
        return null;
    }
}
0 голосов
/ 30 августа 2018

У вас логика наизнанку; это то, что создает вашу проблему. Сделайте это в другом порядке и разбейте все на крошечные методы:

Army GetArmy(ArmyNum a)
{
  switch(a)
  {
    case ArmyNum.army1: return controller.army1;
    ...
  }
}

Unit GetUnit(Army a, UnitNum u)
{
  switch(u)
  {
    case UnitNum.unit1: return a.unit1;
    ...
  }
}

void ChangeAttribute(GameAttribute a, Army a, Unit u)
{
  switch (attributeToChange) {
    case GameAttribute.Accuracy:
      u.accuracy += 1;
      break;
      ...

А теперь звонить на сайт просто:

Army a = GetArmy(activeArmy);
Unit u = GetUnit(a, unitToChange);
ChangeAttribute(attributeToChange, a, u); 

Урок здесь - - создайте небольшие методы, каждый из которых делает одну вещь исключительно хорошо . Затем составьте более сложные методы из этих меньших методов.

Как только вы это сделаете, вы можете начать изменять детали реализации. Например, мы можем заметить, что GetUnit - это метод Army, поэтому мы изменили бы его подпись на

Unit GetUnit(UnitNum u) // Now a method of Army
{
  switch(u)
  {
    case UnitNum.unit1: return this.unit1; // this, not 'a'.
    ...
  }
}

А теперь сайт звонков

Army a = GetArmy(activeArmy);
Unit u = a.GetUnit(unitToChange);
ChangeAttribute(attributeToChange, a, u); 

что явно лучше.

Теперь предположим, что вы делаете Dictionary<ArmyNum, Army> с именем Armies. Затем вы переписываете:

Army GetArmy(ArmyNum a) => Armies[a];

И аналогично для GetUnits. Как только вы окажетесь там, вы сможете найти сайты, где вы используете GetArmy и GetUnit и заменить их на Armies[a] и Armies[a].Units[u] и т. Д.

Ваше представление о движении в мир, где все параметризовано, является хорошей идеей. Постепенно доберитесь , внеся небольших изменений, каждое из которых явно правильное .

Расширенное решение заключается в выполнении действий, связанных с делегатами :

actions = new Dictionary<GameAttribute, Action<Army, Unit>>() 
{
  { Attribute.Accuracy, (a, u) => { u.accuracy += 1; } }, 
  ...
};

и тогда вы могли бы написать:

a = Armies[army];
u = a.Units[unit]
actions[attribute](a, u);

Но иди, прежде чем бежать! По мере того, как логика вашей программы усложняется, вы обнаружите, что вы снова создаете огромную массу сложного кода, и затем вы попытаетесь организовать его в классы, используя хорошие принципы ОО и , которые могут сделать его хуже, Я написал серию статей о том, как использование ОО для решения подобных проблем не работает; если эта тема вас интересует, см. https://ericlippert.com/2015/04/27/wizards-and-warriors-part-one/

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...