Как заставить этот C++ кодировать больше DRY?

У меня есть эти два метода на классе, которые отличаются только по одному вызову метода. Очевидно, это - очень неDRY, тем более, что оба используют ту же формулу.

int PlayerCharacter::getAttack() {
    int attack;
    attack = 1 + this->level;
    for(int i = 0; i < this->current_equipment; i++) {
        attack += this->equipment[i].getAttack();
    }
    attack *= sqrt(this->level);
    return attack;
}
int PlayerCharacter::getDefense() {
    int defense;
    defense = 1 + this->level;
    for(int i = 0; i < this->current_equipment; i++) {
        defense += this->equipment[i].getDefense();
    }
    defense *= sqrt(this->level);
    return defense;
}

Как я могу убрать это в C++?

8
задан Macha 19 April 2010 в 18:58
поделиться

7 ответов

На мой взгляд, то, что у вас есть, вполне подходит, так как это позволит вам настраивать атаку/защиту больше, чем если бы вы представляли их обе одной функцией. Как только вы начнете тестировать свою игру, вы начнете балансировать формулы атаки/защиты, поэтому наличие отдельных функций для них - это нормально.

Вся концепция DRY [не повторяйтесь] заключается в том, чтобы предотвратить превращение вашего кода в огромный copy & paste fest. В вашей ситуации формулы защиты/атаки будут меняться со временем [например, что если у персонажей есть баффы/статусные повреждения? Определенный статусный недуг может снизить защиту вдвое, увеличив при этом атаку на 2 (Берсерк, отсылка к FF, хех)]

.
12
ответ дан 5 December 2019 в 04:36
поделиться

Помимо ответа ltzWarty , я бы порекомендовал преобразовать ваш цикл в функцию для лучшей читаемости:

int PlayerCharacter::getEquipmentAttack() {
    int attack = 0;
    for(int i = 0; i <= current_equipment; i++) {
        attack += this.equipment[i].getAttack();
    }
    return attack;
}
int PlayerCharacter::getAttack() {
    int attack = 1 + this->level;
    attack += getEquipmentAttack();
    attack *= sqrt(this->level);
    return attack;
}

Кроме того, когда вы объявляете атаку локальной переменной , вы должны ] инициализировать его немедленно .

2
ответ дан 5 December 2019 в 04:36
поделиться

Один из простых способов - представить все атрибуты части оборудования в массиве, индексированном перечислением.

enum Attributes {
  Attack, 
  Defense,
  AttributeMAX
};

class Equipment {
  std::vector<int> attributes;

  Equipment(int attack, int defense): attributes(AttributeMAX)
  {
    attributes[ATTACK] = attack;
    attributes[DEFENSE] = defense;
  }

};

Затем вы измените свою функцию на

int PlayerCharacter::getAttribute(int& value, Attribute attribute) {
    value = 1 + this->level;
    for(int i = 0; i <= current_equipment; i++) {
        value += this->equipment[i].attributes[attribute];
    }
    value *= sqrt(this->level);
    return value;
}

И вы можете назвать ее так

  player.getAttribute(player.attack, Attack);
18
ответ дан 5 December 2019 в 04:36
поделиться

В зависимости от другого кода в приложении это может стоить того, а может и не стоить, но подход ООП будет делать объекты значений защиты и атак класса, а не простого int . Затем вы можете получить их из общего базового класса, у которого есть метод get (), который вызывает виртуальный метод getEquipmentRate (), определенный каждым из подклассов по мере необходимости.

3
ответ дан 5 December 2019 в 04:36
поделиться

ну, я бы хотя бы подумал об извлечении sqrt (this.level); как отдельная функция с именем getLevelModifier ()

и

defense = 1 + this.level;

attack = 1 + this.level;

может быть

defense = getBaseDefense();

attack= getBaseAttack();

Это не только добавляет гибкости, но и автоматически документирует вашу функцию.

5
ответ дан 5 December 2019 в 04:36
поделиться

С точки зрения строгого рефакторинга вы могли бы сделать это:

int PlayerCharacter::getDefense() {
    return getAttribute(&EquipmentClass::getDefense);
}

int PlayerCharacter::getOffense() {
    return getAttribute(&EquipmentClass::getOffense);
}

int PlayerCharacter::getAttribute(int (EquipmentClass::*attributeFun)()) {
    int attribute = 0;
    attribute= 1 + this->level;
    for(int i = 0; i <= current_equipment; i++) {
        attribute += this->equipment[i].*attributeFun();
    }
    attribute *= sqrt(this->level);
    return attribute;
}
6
ответ дан 5 December 2019 в 04:36
поделиться

IMO, ItzWarty делает разумное замечание - вы можете просто оставить код в покое. Предполагая, что вы решите, что изменение - это хорошо, вы можете сделать что-то вроде этого:

class equipment { 
public:
    int getAttack();
    int getDefense();
};

int PlayerCharacter::getBattleFactor(int (equipment::*get)()) { 
    int factor = level + 1;
    for (int i=0; i<current_equipment; ++i)
        factor += equipment[i].*get();
    return factor * sqrt(level + 1);
}

Вы бы назвали это так:

int attack = my_player.getBattleFactor(&equipment::getAttack);

или:

int defense = my_player.GetBattleFactor(&equipment::getDefense);

Изменить:

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

class PlayerCharacter {
    std::vector<equipment> d_equip;
    std::vector<equipment> o_equip;

// ...

int d=level+1+std::accumulate(d_equip.begin(), d_equip.end(), 0)*sqrt(level+1);

int o=level+1+std::accumulate(o_equip.begin(), o_equip.end(), 0)*sqrt(level+1);
2
ответ дан 5 December 2019 в 04:36
поделиться
Другие вопросы по тегам:

Похожие вопросы: