代码之家  ›  专栏  ›  技术社区  ›  Priyank

设计与对象责任问题

  •  1
  • Priyank  · 技术社区  · 16 年前

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

    1. 我有一个机器人物体,它可以自己横穿地面。它将提供移动指令,并且必须相应地进行解析。例如,样本输入为: a.旋转右移动

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

    我用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轴移动,我的坐标应该在+ELSE中,在-X轴相同。

    1. 目前,改变月球车表面的责任被间接地委托给坐标系和方向类。这是真的吗?路虎不负责保持方向吗?在我的设计中,我把责任下放给协调和指导类,真的是对的吗?仅仅因为在那里更容易操纵它?

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

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

    5 回复  |  直到 16 年前
        1
  •  2
  •   redtuna    16 年前

    你在问如何简化。如果我可以建议一些大胆的东西,为什么不使用一个不透明的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字段来存储它所面对的方向。

        2
  •  4
  •   Carl Manaster    16 年前

    这是我前几天提出的一个方向清单,我可能非常喜欢它。也许您会发现它在您的代码中很有用。

    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);
        }
    }
    
        3
  •  1
  •   Roland Ewald    16 年前

    当我看到这段代码时,首先想到的是,方向不应该有字符串字段方向值,而应该是一个存储指南针的字段(即,指南针.east、指南针.west)。这将使您摆脱movestate.action()中的字符串比较,从而使代码更加清晰。

    命名似乎也有问题:也许北、东、西和南应该在一个名为direction(而不是compass)的枚举中,而directionOnRight()等应该在当前方向实现其静态方法(将当前方向作为单个参数获取,并返回右/左/反方向)?您不需要将它们存储在额外的字段imho中(记住关于过早优化的说法;-)。

        4
  •  1
  •   Brian Agnew    16 年前

    我一看到这件事就立刻想到了一些困惑。Rover级别有4个状态和一个方向,这似乎有点违反直觉。我期待一个职位和一个方向(对于州来说,我可能期待,开/关/充电或类似的事情)。

    所以,我会调查 Java enums 有北/南/东/西 Direction 方向的枚举。位置(坐标)有X/Y位置,为了移动,我只需要实现一个 deltaX() deltaY() 面对面枚举(看起来卡尔刚刚发布了类似的内容)

    然后您的移动代码将简单地如下所示:

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

    无论你面对哪个方向。注意这个 委派行动。漫游者总是移动,但是 方向 枚举赋予它要更改的dx/dy。

    枚举也可以有方法 clockwise() counterClockwise() 如此呼唤 NORTH.clockwise() 将返回您的新面值 EAST . 每个枚举实例将只有delta和顺时针/逆时针方法,以及 Rover 只需以下几点:

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

    这看起来更直观,我也希望如此。我已经分别表示了x和y,但您可能希望用一个类来包装。如果这样做,那么方向枚举应该处理这样一个对象,而不是依赖于它再次分解为x和y。

        5
  •  0
  •   Yaroslav Yakovlev    16 年前

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