设计和对象责任的问题

时间:2009-08-06 18:39:35

标签: java design-patterns oop

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

  1. 我有一个Robot对象,假设它自己遍历地面。它将提供移动指令,并且必须相应地进行解析。例如,样本输入将是: 一个。 RotateRight |移动| RotateLeft |移动|移动|移动
  2. 移动是网格上的单位移动。

    我在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轴相同。

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

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

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

5 个答案:

答案 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能够在任何步骤范围内移动。

相关问题