如何使这个C ++代码更干?

时间:2010-04-19 18:41:40

标签: c++ dry code-cleanup

我在一个只有一个方法调用不同的类上有这两个方法。显然,这是非常不干的,特别是因为两者都使用相同的公式。

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 ++中整理它?

7 个答案:

答案 0 :(得分:18)

一种简单的方法是在数组中表示设备的所有属性,由枚举索引。

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);

答案 1 :(得分:12)

在我看来,你所拥有的一切都很好,因为它可以让你更多地调整攻击/防御,而不是用一个函数代表它们。一旦你开始测试你的游戏,你将开始平衡攻击/防御公式,所以为它们提供单独的功能是好的。

DRY [不重复自己]的整个概念[希望]可以防止你的代码成为一个庞大的副本&amp;粘贴节日。在你的情况下,防御/攻击公式会随着时间而改变[例如,如果角色有buff /状态 - 疾病怎么办?特定状态疾病可能会将防御减少一半,同时将攻击力提高2(Berserk,FF参考,heh)]

答案 2 :(得分:6)

从严格的重构角度来看,你可以这样做:

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;
}

答案 3 :(得分:5)

好吧,我至少会考虑将sqrt(this.level);解压缩为一个名为getLevelModifier()的单独函数

defense = 1 + this.level;

attack = 1 + this.level;

可能是

defense = getBaseDefense();

attack= getBaseAttack();

这不仅增加了灵活性,还自动记录您的功能。

答案 4 :(得分:3)

根据应用程序中的其他代码,它可能或可能不值得,但OOP方法会使类的防御和攻击值对象而不是普通的int。然后,您可以从具有get()方法的公共基类派生它们,该方法根据需要调用由每个子类定义的虚拟getEquipmentRate()方法。

答案 5 :(得分:2)

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);

答案 6 :(得分:2)

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;
}

此外,当您声明本地变量attack时,您应该initialize it immediately