如果您需要更多逻辑解释让我知道。
private void movePosistion(Plant p) {
/*
* set which direction to move,the number generated relates to the
* direction as below:
* 1 2 3
* 4 5
* 6 7 8
*/
int xdiff = p.getXpos() - xpos;
int ydiff = p.getYpos() - ypos;
if (xdiff > 0){
if (ydiff > 0){
//8
xpos += 30;
ypos += 30;
}else if(ydiff < 0){
//3
xpos += 30;
ypos -= 30;
}else{
//5
xpos += 30;
}
}else if(xdiff < 0){
if (xdiff > 0){
//6
xpos -= 30;
ypos += 30;
}else if(xdiff < 0){
//1
xpos -= 30;
ypos -= 30;
}else{
//4
xpos -= 30;
}
}else{
if (ydiff < 0){
//7
ypos -= 30;
}else{
//2
ypos += 30;
}
}
if (xpos > 720)
xpos = 1;
if (ypos > 720)
ypos = 1;
if (xpos < 1)
xpos = 720;
if (ypos < 1)
ypos = 720;
}
#1 楼
这是我的重构:private void movePosition(Plant p) {
xpos += Integer.signum(p.getXpos() - xpos) * DELTA_X;
ypos += Integer.signum(p.getYpos() - ypos) * DELTA_Y;
xpos = Math.floorMod(xpos, MAX_X);
ypos = Math.floorMod(ypos, MAX_Y);
}
1。 Signum
signum
实现了sign函数,对于负整数,零和正整数分别给出-1、0或1。它以非常简短易读的表达式对原始逻辑进行编码。符号乘以适当数量的单位(代码中未详细说明常数,顺便说一句)。
我什么都没有原则上反对决策表(请参阅rolfl的答案),但我认为这不是必需的。我也可以做到!
摘自Code Complete 2nd Edition,第19章:一般控制问题,第431页:
使用决策表替换复杂条件
有时您会进行涉及多个变量的复杂测试。 [...]
...,有时你不知道;-)
2。模量
原始代码的环绕行为可以通过模运算来实现。请注意,您不能仅在Java中使用
%
运算符,因为它会计算余数,当您的位置低于零时,它可以为负。 floorMod
操作实际上是在计算模块化算术。现在,您可能会认为:这是错误的,原始代码无法像这样工作!
首先,OP的坐标范围是1到720(包括两端)。我对此有疑问,因此在此故意更改了方法。原因是,在原始代码中,不是使用原点为(0,0)的坐标空间,而是将原点平移为(1,1)。
大多数情况下,您最终不得不向操作中添加或减去1。最终可能导致偏离一对一的错误。但是,如果坐标是从0到719,则环绕逻辑仅由模运算给出。
您可能会说:“但是原始行为不像模数!”为什么这样说因为,假设
x
是710,然后我加30:使用模数,我回到20,而使用OP的代码,我将是1,因为当我们越界时,我们回到最小(或最大)位置。为此,我回答说,您永远不在710位置,而仅在0、30、60,...,690位置。至少,这是我从OP的代码中了解到的,该对象似乎在网格上移动,而不是自由移动。我想该对象最初始终位于30的倍数,然后只能移动30个单位。
如果我错了,那么(1)对不起,(2)是的,模数并不是完全正确的答案;您最好使用rolfl的
boundPos
函数。评论
\ $ \ begingroup \ $
我真的很喜欢这个答案,主要是因为它的简单性,给出的所有答案都是很好的,而且采取的方法也非常不同。
\ $ \ endgroup \ $
–扎克·鲍威尔(Zac Powell)
2014年4月11日12:54
#2 楼
我同意@rolfl,您需要提取方法并使用决策表。摘自Code Complete第二版,第19章:常规控制问题,第431页:
使用决策表来替换复杂的条件
有时您具有包含多个变量的复杂测试。使用决策表执行测试而不是使用if或case可能会有所帮助。决策表查找起初更容易编码,只需要几行代码,而没有棘手的控制结构。复杂性的这种最小化使出错的机会最小化。如果数据发生更改,则可以更改决策表而无需更改代码。您只需要更新数据结构的内容即可。
为了便于阅读,在这里我会更加详细。首先,列举一个可能的区别:
public enum Difference {
ZERO,
BELOW_ZERO,
ABOVE_ZERO
}
然后是一个包含可用规则的类:
public class MoveLogic {
private final Map<Rule, Move> rules = new LinkedHashMap<>();
public MoveLogic() {
addRule(Difference.ABOVE_ZERO, Difference.ABOVE_ZERO, 30, 30); // 8
addRule(Difference.ABOVE_ZERO, Difference.BELOW_ZERO, 30, -30); // 3
addRule(Difference.ABOVE_ZERO, Difference.ZERO, 30, 0); // 5
addRule(Difference.BELOW_ZERO, Difference.ABOVE_ZERO, -30, 30); // 6
addRule(Difference.BELOW_ZERO, Difference.BELOW_ZERO, -30, -30); // 1
addRule(Difference.BELOW_ZERO, Difference.ZERO, -30, 0); // 4
addRule(Difference.ZERO, Difference.BELOW_ZERO, 0, -30); // 7
addRule(Difference.ZERO, Difference.ABOVE_ZERO, 0, 30); // 2
}
private void addRule(Difference xDifference, Difference yDifference, int x, int y) {
rules.put(new Rule(xDifference, yDifference), new Move(x, y));
}
public Move getMove(Difference xDifference, Difference yDifference) {
return rules.get(new Rule(xDifference, yDifference));
}
}
用法:
final Difference xDifference = calculateDifference(plant.getXpos(), xpos);
final Difference yDifference = calculateDifference(plant.getXpos(), ypos);
final Move move = moveLogic.getMove(xDifference, yDifference);
xpos += move.getX();
ypos += move.getY();
最后,创建
Difference
引用的辅助方法:private Difference calculateDifference(final int plantPosition, final int position) {
final int difference = plantPosition - position;
if (difference > 0) {
return Difference.ABOVE_ZERO;
}
if (difference < 0) {
return Difference.BELOW_ZERO;
}
return Difference.ZERO;
}
如
Rule
:public class Rule {
private Difference xDifference;
private Difference yDifference;
public Rule(Difference xDifference, Difference yDifference) {
this.xDifference = xDifference;
this.yDifference = yDifference;
}
// generate hashCode and equals, the Map needs it!
}
和
Move
:public class Move {
private int x;
private int y;
public Move(int x, int y) {
this.x = x;
this.y = y;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
}
看起来似乎太冗长,但可以避免重复出现以及具有索引和数组的魔术计算。
评论
\ $ \ begingroup \ $
似乎太冗长了。老实说,我什至会说过度设计。为什么要在运行时表示规则(这不在规范中)?为什么在类中编码“差异”的概念?为什么要采用复杂的方法?不要添加仅设置和获取值的getter / setter:使用公共字段;将来,只要有必要,您总是可以朝着更高的复杂性发展,但前提是有必要。这只是我的意见,希望对您有所帮助:-)
\ $ \ endgroup \ $
– coredump
2014-4-10 21:11
#3 楼
预计算可能的动作,并将条件转换为简单的数学表达式是解决此类问题的好方法。您的代码也遭受重复块和重复逻辑的困扰:DRY-Don' t重复一遍。
通过提取函数来解决这个问题。
最后,您的代码中充满了魔幻数字,容易泄漏错误.....
考虑以下代码,尽管在代码行上比您的代码长,但实际上更具可读性(我承认,复杂的2D数组除外)。
/>
评论
\ $ \ begingroup \ $
@coredump-谢谢。。。你是对的。立即修复。
\ $ \ endgroup \ $
–rolfl
2014年4月10日在20:42
\ $ \ begingroup \ $
尽管原则上说表是一种好方法,但我认为这里不需要这样做。看到我的答案(我重用boundPos,顺便说一句)
\ $ \ endgroup \ $
– coredump
2014年4月10日在20:58
\ $ \ begingroup \ $
@coredump-很好。您的答案也是。您应该访问2nd Monitor聊天室以发现Code Review的另一面。
\ $ \ endgroup \ $
–rolfl
2014年4月11日上午10:51
#4 楼
这是我的看法。由于x和y中的运动是独立的,因此我为一个方向的运动定义了一个类。但是您至少可以使用x和y中的运动是独立的想法。
评论
\ $ \ begingroup \ $
在Coordinate构造函数中,为什么当initialValue为负数时也不抛出异常?
\ $ \ endgroup \ $
– coredump
2014年4月10日在21:35
\ $ \ begingroup \ $
已更正。我想还有许多其他问题需要修复。例如,目前可以将Closer移至具有不同worldSize的坐标。
\ $ \ endgroup \ $
–toto2
2014年4月10日在22:28
评论
对于某些x,您的头寸是否总是“ 1 + 30x”?我认为这是一个决定,最终可能会导致一个错误。通常,如果宽度为720点,则坐标的范围最好为0到719;这样,数学运算就可以更自然地拟合,而不必添加或删除1;例如,您可以在最后几行中使用模数运算符:xpos = xpos%720。我编辑了答案以提供更多解释。
我刚刚注意到该函数称为“ movePosistion”;我在回答中更正了它。顺便说一句,感谢您接受它。