2009-08-06 72 views
1

我有一个设计和对象结构相关的问题。这里是问题陈述:设计和对象责任的问题

  1. 我有一个机器人对象,它是假设自己遍历地面。它将提供移动指令并且必须相应地进行解析。例如,示例输入将是: a。 RotateRight |移动| RotateLeft |移动|移动|移动

移动是网格上的单位移动。

我在java中做了一个非常基本的设计。 (完整的代码粘贴下面)

package com.roverboy.entity; 

import com.roverboy.states.RotateLeftState; 
import com.roverboy.states.RotateRightState; 
import com.roverboy.states.State; 

public class Rover { 

    private Coordinate roverCoordinate; 
    private State roverState; 

    private State rotateRight; 
    private State rotateLeft; 
    private State move; 

    public Rover() { 
     this(0, 0, Compass.NORTH); 
    } 

    public Rover(int xCoordinate, int yCoordinate, String direction) { 
     roverCoordinate = new Coordinate(xCoordinate, yCoordinate, direction); 
     rotateRight = new RotateRightState(this); 
     rotateLeft = new RotateLeftState(this); 
     move = new MoveState(this); 
    } 

    public State getRoverState() { 
     return roverState; 
    } 

    public void setRoverState(State roverState) { 
     this.roverState = roverState; 
    } 

    public Coordinate currentCoordinates() { 
     return roverCoordinate; 
    } 

    public void rotateRight() { 
     roverState = rotateRight; 
     roverState.action(); 
    } 

    public void rotateLeft() { 
     roverState = rotateLeft; 
     roverState.action(); 
    } 

    public void move() { 
     roverState = move; 
     roverState.action(); 
    } 
} 


package com.roverboy.states; 

public interface State { 

    public void action(); 
} 

package com.roverboy.entity; 

import com.roverboy.states.State; 

public class MoveState implements State { 

    private Rover rover; 

    public MoveState(Rover rover) { 
     this.rover = rover; 
    } 

    public void action() { 
     rover.currentCoordinates().setXCoordinate(
       (Compass.EAST).equalsIgnoreCase(rover.currentCoordinates() 
         .getFacingDirection()) ? rover.currentCoordinates() 
         .getXCoordinate() + 1 : rover.currentCoordinates() 
         .getXCoordinate()); 

     rover.currentCoordinates().setXCoordinate(
       (Compass.WEST).equalsIgnoreCase(rover.currentCoordinates() 
         .getFacingDirection()) ? rover.currentCoordinates() 
           .getXCoordinate() - 1 : rover.currentCoordinates() 
           .getXCoordinate()); 

     rover.currentCoordinates().setYCoordinate(
       (Compass.NORTH).equalsIgnoreCase(rover.currentCoordinates() 
         .getFacingDirection()) ? rover.currentCoordinates() 
           .getYCoordinate() + 1 : rover.currentCoordinates() 
           .getYCoordinate()); 

     rover.currentCoordinates().setYCoordinate(
       (Compass.SOUTH).equalsIgnoreCase(rover.currentCoordinates() 
         .getFacingDirection()) ? rover.currentCoordinates() 
           .getYCoordinate() - 1 : rover.currentCoordinates() 
           .getYCoordinate()); 
    } 
} 


package com.roverboy.states; 

import com.roverboy.entity.Rover; 

public class RotateRightState implements State { 

    private Rover rover; 

    public RotateRightState(Rover rover) { 
     this.rover = rover; 
    } 

    public void action() { 
     rover.currentCoordinates().directionOnRight(); 
    } 

} 

package com.roverboy.states; 

import com.roverboy.entity.Rover; 

public class RotateLeftState implements State { 

    private Rover rover; 

    public RotateLeftState(Rover rover) 
    { 
     this.rover = rover; 
    } 

    public void action() { 
     rover.currentCoordinates().directionOnLeft(); 
    } 

} 


package com.roverboy.entity; 

public class Coordinate { 

    private int xCoordinate; 
    private int yCoordinate; 
    private Direction direction; 
    { 
     Direction north = new Direction(Compass.NORTH); 
     Direction south = new Direction(Compass.SOUTH); 
     Direction east = new Direction(Compass.EAST); 
     Direction west = new Direction(Compass.WEST); 
     north.directionOnRight = east; 
     north.directionOnLeft = west; 
     east.directionOnRight = north; 
     east.directionOnLeft = south;  
     south.directionOnRight = west; 
     south.directionOnLeft = east; 
     west.directionOnRight = south; 
     west.directionOnLeft = north; 
     direction = north; 
    } 

    public Coordinate(int xCoordinate, int yCoordinate, String direction) { 
     this.xCoordinate = xCoordinate; 
     this.yCoordinate = yCoordinate; 
     this.direction.face(direction); 
    } 

    public int getXCoordinate() { 
     return xCoordinate; 
    } 
    public void setXCoordinate(int coordinate) { 
     xCoordinate = coordinate; 
    } 
    public int getYCoordinate() { 
     return yCoordinate; 
    } 
    public void setYCoordinate(int coordinate) { 
     yCoordinate = coordinate; 
    } 

    public void directionOnRight() 
    { 
     direction.directionOnRight(); 
    } 

    public void directionOnLeft() 
    { 
     direction.directionOnLeft(); 
    } 

    public String getFacingDirection() 
    { 
     return direction.directionValue; 
    } 
} 

class Direction 
{ 
    String directionValue; 
    Direction directionOnRight; 
    Direction directionOnLeft; 

    Direction(String directionValue) 
    { 
     this.directionValue = directionValue; 
    } 

    void face(String directionValue) 
    { 
     for(int i=0;i<4;i++) 
     { 
      if(this.directionValue.equalsIgnoreCase(directionValue)) 
       break; 
      else 
       directionOnRight(); 
     } 
    } 

    void directionOnRight() 
    { 
     directionValue = directionOnRight.directionValue; 
     directionOnRight = directionOnRight.directionOnRight; 
     directionOnLeft = directionOnRight.directionOnLeft;    
    } 

    void directionOnLeft() 
    { 
     directionValue = directionOnLeft.directionValue; 
     directionOnRight = directionOnLeft.directionOnRight; 
     directionOnLeft = directionOnLeft.directionOnLeft;  
    } 
} 

现在我的疑问是这个最后一堂课“方向”和“协调”。坐标代表流动站的坐标对象,有助于保持方向。目前为了跟踪方向,我正在使用方向对象的双向链接列表,它几乎像指南针一样工作。向左或向右旋转。

这是我的问题。 1.我已经使用状态模式和显示的方向跟踪设计。有更好的方法来简化这个吗?雷姆。我需要正确保持坐标;这样如果你朝着+ y轴移动,我的坐标应该在+其他的减号中。 X轴相同。

  1. 目前,改变漫游者面部的责任是间接地委托给坐标和方向类。这真的是对的吗?流动站是否负责维持方向?在我的设计中,我是否真的把责任委托给协调和指导课程是正确的;只是因为在那里操纵它更容易?

  2. 任何简单的设计改进和代码建议都会受到欢迎。随意批评。

感谢您的耐心和反馈;提前。

+0

另请参阅http://smlnj.org/icfp08-contest/task.html – finnw 2009-08-06 18:53:36

+0

编辑器可以清除此问题吗?标题太广泛了,当它看起来他正在处理算法问题时,OP正在寻求设计帮助。 – 2009-08-06 18:55:09

回答

1

你在问如何简化。如果我可能会建议一些粗体的东西,为什么不用一个不透明的int来指示方向,并且有一个静态类来处理呢?通过“opaque int”我的意思是你的代码永远不会直接使用它,而只能作为Direction类的参数。

下面是一些部分java风格的伪代码,以显示我的意思。

// 0 = east, 1 = north, 2 = west, ... 
public class Direction { 
    static int [] moveX = [ 1, 0, -1, 0]; 
    static final int NORTH = 1; 
    // coordinates after moving one step in the given direction 
    static Pair move(int direction, Pair old) { 
    return new Pair(old.x + moveX[direction] , old.y + moveY[direction]); 
    } 
    static int turnLeft(int direction) { 
    return (direction+1) % 4; 
    } 
    static int turnRight(int direction) { 
    return (direction+3) % 4; 
    } 
} 

做事必须使用更少的分配优势的这种方式,使垃圾收集器将不再需要经常运行。另一个优点是,设计仍然是面向对象的,因为如果您希望以后能够旋转,您可以轻松地更改方向类。 45度一次。

为了回答您的其他问题,我认为向方向类委派沿特定方向改变坐标的任务是完全正确的。流动站只负责维持方向,因为漫游车对象将包含一个int域来存储它所面对的方向。

+0

您认为垃圾回收在这种情况下是一个实际问题吗? – 2009-08-06 19:03:30

+0

你知道吗,Brian,再次查看原始代码,我认为它可以避免垃圾回收问题,因为它不会分配。所以在这种情况下,它很好。如果你问的是一般的话,那么按照我的经验来说,如果你想要尽可能地减少GC,那么顺利的进行游戏就是了。 – redtuna 2009-08-06 19:08:46

1

第一件事当我看到这段代码时,我想到的是Direction不应该有一个String字段directionValue,而是一个存储Compass(即Compass.EAST,Compass.WEST)的字段。这会让你摆脱MoveState.action()中的字符串比较,因此应该让你的代码更清晰。

在命名方面似乎也有一个问题:NORTH,EAST,WEST和SOUTH应该在一个名为Direction(而不是Compass)的枚举中,而DirectionOnRight()等在当前的Direction实现中应该是它的静态方法(获取当前方向作为单个参数,并返回右/左/反方向)?你真的不需要将它们存储在额外的领域恕我直言(记住关于过早优化的说法;-)。

4

这里是一个方向枚举我提出了前一天,其中我可能非常喜欢。也许你会发现它在你的代码中很有用。

import java.awt.Point; 

public enum Direction { 
    E(1, 0), N(0, 1), W(-1, 0), S(0, -1); 
    private final int dy; 
    private final int dx; 

    private Direction(int dx, int dy) { 
     this.dx = dx; 
     this.dy = dy; 
    } 

    public Direction left() { 
     return skip(1); 
    } 

    public Direction right() { 
     return skip(3); 
    } 

    public Direction reverse() { 
     return skip(2); 
    } 

    private Direction skip(int n) { 
     final Direction[] values = values(); 
     return values[(ordinal() + n) % values.length]; 
    } 

    public Point advance(Point point) { 
     return new Point(point.x + dx, point.y + dy); 
    } 
} 
1

我的这种想法在眼前是一些混乱。罗孚班有4个州和一个方向,这似乎有点反直觉。我期望一个立场和一个方向(对于我所期望的状态,ON/OFF/RECHARGING或类似情况)。

所以,我会调查Java enums并有一个NORTH/SOUTH/EAST/WEST Direction枚举的方向。 (坐标)的位置具有X/Y位置和移动,我会简单地实现对面向枚举deltaX()deltaY()(它看起来像卡尔刚刚发布类似的东西)

那么你的移动代码只会像:

x += facing.deltaX() 
y += facing.deltaY() 

无论你朝哪个方向。注意这个不是委托的动作。流浪者总是在移动,但是枚举给出了改变的dx/dy。

枚举也可以有方法clockwise()counterClockwise(),因此调用NORTH.clockwise()将返回新的面向值EAST。每个枚举实例只会有增量,顺时针/逆时针方法,以及你的Rover只是有以下几点:

private Direction facing; 
private int x; 
private int y; 

这似乎更直观,我所期待的。我已经分别表达了x和y,但是您可能想要包装在一个类中。如果你这样做,那么方向枚举应该处理这样一个对象,而不是依靠它被再分解为x和y。

0

对我来说这似乎太复杂了。我认为应该这样做:让你的机器人知道他的转向角度。然后,如果他被要求左转或右转,他会改变这个角度。当他被要求移动时,他将根据这个角度在x,y坐标上移动。角度可以像罗盘一样存储,甚至可以用实际角度(0,90,180,270)更简单地存储。通过在sin(角度)和cos(角度)上乘以运动步长,可以轻松地在角度方向上移动机器人。 t it be that simple? It will also handle more directions that just 4 and you为什么能够在任何步进范围内移动。