我在一个类上有这两个方法,它们只在一个方法调用中不同。显然,这是非常不干燥的,特别是因为两者使用相同的公式。有人能给我关于如何收拾这件事的建议吗?
public class PlayerCharacter {
int level;
public int getAttack() {
int attack = 1 + level;
for(Equipment e : equipments)
attack += e.getAttack();
return attack * Math.sqrt(level);
}
public int getDefense() {
int attack = 1 + level;
for(Equipment e : equipments)
attack += e.getDefense();
return attack * Math.sqrt(level);
}
}我考虑将循环重构为外部方法,但不同的方法调用使我感到困惑。
发布于 2014-07-17 11:11:21
老实说,我不确定我是否会费心把这些方法合并。是的,它们本质上是相同的,但是仔细看一下,重复的代码仍然只有几行,其中大部分是你所使用的语言和类设计中不可避免的样板代码。
通常,当考虑是否将一些(看似)重复的代码干涸时,我建议先问自己几个问题:
尽管如此,如果我真的相信这两种方法之间的相似性是基本的,而不仅仅是偶然的,而且如果我真的想消除冗余,那么我要做的(至少首先)是将共享的(1 + level + sum(equipment)) * sqrt(level)公式分解成一个单独的(静态)方法,留给我:
public class PlayerCharacter {
private int level;
public int getAttack() {
int attackBonus = 0;
for (Equipment e : equipments) attackBonus += e.getAttack();
return powerFormula(level, attackBonus);
}
public int getDefense() {
int defenseBonus = 0;
for (Equipment e : equipments) defenseBonus += e.getDefense();
return powerFormula(level, defenseBonus);
}
private static int powerFormula(int level, int bonus) {
return (1 + level + bonus) * (int) Math.sqrt(level);
}
}(使powerFormula()方法静态的选择主要是一种文体选择,但我认为应该强调的是,它只是封装了一个简单的数学公式,该公式独立于任何特定的字符对象。)
现在,仍然有相当多的明显重复,但这实际上只是总结球员的装备的攻击/防御奖金的样板。在Java8中,我们可以通过使用方法参考来清理剩余的复制,例如:
public class PlayerCharacter {
private int level;
public int getAttack() {
int attackBonus = getEquipmentBonus(Equipment::getAttack);
return powerFormula(level, attackBonus);
}
public int getDefense() {
int defenseBonus = getEquipmentBonus(Equipment::getDefense);
return powerFormula(level, defenseBonus);
}
private int getEquipmentBonus(ToIntFunction<Equipment> func) {
return equipments.stream().mapToInt(func).sum();
}
private static int powerFormula(int level, int bonus) {
return (1 + level + bonus) * (int) Math.sqrt(level);
}
}(我希望这段代码是正确的,因为我实际上没有Java 8编译器来测试它。通过将getEquipmentBonus()和powerFormula()方法合并成一个方法,它可以进一步缩短,但是保持它们的分离对我来说更清晰--这样,每种方法都有一个明确的职责。对于getEquipmentBonus()方法,我选择使用流接口,但您也可以使用一个简单的循环。)
对于早期的Java版本,重复循环似乎是不可避免的,除非您想使用内部类来模拟Java 8 lambda表达式或类似的东西。
在任何情况下,上述两个重构版本都实现了这样的目标:如果您想要更改玩家攻击/防御能力公式,则只能在一个地方更改该公式,并且更改将影响两种计算。如果这不是您想要的,您可能会更好地保留原来的代码。
发布于 2014-07-16 22:51:37
检查这些相似之处,我建议您将攻击/防御属性分解为一个单独的类。然后,可以将其用作参数。enum是该属性的优秀候选--我将称之为Stance,我相信您会想出一个更好的名称。
结果非常简洁,我认为非常简洁:
enum Stance {
Attack {
@Override
int getStrength(Equipment equipmemt) {
// When on attack - deliver the attackng strength of the equipment.
return equipmemt.getAttack();
}
},
Defend {
@Override
int getStrength(Equipment equipmemt) {
// When in defence - deliver the defensive strength of the equipment.
return equipmemt.getDefence();
}
};
// All stances must delver a value depending on the equipment.
abstract int getStrength(Equipment equipmemt);
// Convenience method for collections of Equipment.
int getStrength(Iterable<Equipment> es) {
int total = 0;
for (Equipment e : es) {
total += getStrength(e);
}
return total;
}
}
public class PlayerCharacter {
int level;
public double getStrength(Stance stance) {
// Sum all strengths of all equipment in that stance.
int strength = 1 + level + stance.getStrength(equipments);
// Work out the value.
return strength + Math.sqrt(level);
}
public double getAttack() {
// It's the attacking strength.
return getStrength(Stance.Attack);
}
public double getDefense() {
// It's the defending strength.
return getStrength(Stance.Defend);
}
}https://codereview.stackexchange.com/questions/57177
复制相似问题