我有一个设计和对象结构相关的问题。这是问题陈述:
移动是网格上的单位移动。
我在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;
}
}
现在我怀疑最后一节课“方向”和“坐标”。 coordinate表示流动站的坐标对象,有助于保持其方向。目前为了跟踪方向,我正在使用一个双向链接的Direction对象列表,它几乎像指南针一样工作。向左或向右旋转。
以下是我的问题。 我使用状态模式和显示的方向跟踪设计。是否有更好的方法来简化这个?雷姆。我需要正确地保持坐标;这样,如果你向+ y轴移动,我的坐标应该在+中的其他位置。 X轴相同。
目前,更改流动站面部的责任是间接委托给坐标和方向类。这真的是对的吗?漫游者不负责维持方向吗?我是否真的在设计中将责任委托给协调和指导课程;只是因为它更容易操纵它?
欢迎任何简单的设计改进和代码建议。随意批评。
感谢您的耐心和反馈;提前。
答案 0 :(得分: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 :(得分:2)
你在问如何简化。如果我可以建议一些粗体,为什么不使用opaque 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度。
为了回答你的其他问题,我认为将Direction类沿某个方向改变的任务委托给Direction类是完全没问题的。只有在流动站对象包含一个int字段来存储它所面对的方向时,流动站才负责维护方向。
答案 2 :(得分:1)
第一件事当我看到这段代码时,我想到的是Direction不应该有一个String字段directionValue,而是一个存储Compass的字段(即Compass.EAST,Compass.WEST)。这将使您摆脱MoveState.action()中的字符串比较,因此应该使您的代码更加清晰。
命名似乎也有问题:也许NORTH,EAST,WEST和SOUTH应该在名为Direction(而不是Compass)的枚举中,而当前Direction实现中的directionOnRight()等应该是静态的方法(将当前方向作为单个参数,并返回右/左/反方向)?你真的不需要将它们存储在额外的字段IMHO中(记住关于过早优化的说法; - )。
答案 3 :(得分:1)
我看到这一点后立即想到了一些困惑。 Rover类有4个状态和一个方向,这似乎有点反直觉。我期待一个位置和一个方向(对于我可能期望的状态,开/关/重新开始或类似的东西)。
所以,我会调查Java enums,并为方向设置一个NORTH / SOUTH / EAST / WEST Direction
枚举。位置(坐标)有x / y位置,要移动,我只需在面对的枚举上实现deltaX()
和deltaY()
(看起来Carl刚刚发布了类似的东西)
然后您的移动代码看起来就像:
x += facing.deltaX()
y += facing.deltaY()
你面对的任何方向。请注意,此不委托移动。漫游者总是移动,但Direction
枚举给它改变了dx / dy。
枚举也可以包含方法clockwise()
和counterClockwise()
,因此调用NORTH.clockwise()
会返回您的新面值EAST
。每个枚举实例只有delta和顺时针/逆时针方法,而Rover
只有以下内容:
private Direction facing;
private int x;
private int y;
这看起来更直观,也是我所期待的。我已经分别表达了x和y,但你可能想要包装在一个类中。如果你这样做,那么Direction枚举应该处理这样一个对象,而不是依赖它再次分解成x和y。
答案 4 :(得分: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
能够在任何步骤范围内移动。