2009-08-06 2 views
1

나는 디자인과 객체 구조 관련 질문을 가지고있다. 다음은 문제 설명입니다.디자인과 객체 책임에 대한 질문

  1. 나는 자체적으로 땅을 가로 지르는 로봇 개체를 가지고 있습니다. 그것은 이동 명령을 제공하고 그에 따라 해석해야합니다. 예를 들어 샘플 입력은 다음과 같습니다. a. RotateRight | Move | RotateLeft | Move | Move | Move

여기서 move는 그리드의 단위 이동입니다.

나는 자바로 아주 기본적인 디자인을했다.

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

+0

편집기가이 질문을 정리할 수 있습니까? 제목이 너무 넓어서 OP가 알고리즘 문제를 다루는 것처럼 보일 때 디자인 도움말을 요청하고 있습니다. –

답변

1

단순화하는 방법을 묻습니다. 내가 무언가를 굵게 제안한다면 방향에 불투명 int를 사용하지 말고 정적 클래스를 다루는 것이 어떻겠습니까? "opaque int"는 코드에서 직접 사용하지 않고 Direction 클래스의 인수로만 사용한다는 것을 의미합니다.

다음은 부분적인 자바 스타일의 의사 코드로 내 뜻을 보여줍니다.

// 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 클래스에 특정 방향으로 좌표를 변경하는 작업을 위임하는 것이 좋습니다. 로버는 로버 객체가 직면하는 방향을 저장하기위한 int 필드를 포함한다는 의미에서만 방향을 유지할 책임이 있습니다.

+0

가비지 수집이이 시나리오에서 실질적인 문제라고 생각하십니까? –

+0

아시다시피 브라이언은 원래 코드를 다시보고 가비지 수집 문제로 인해 안전하다고 생각합니다. 할당하지 않기 때문입니다. 그래서이 경우에는 괜찮습니다. 일반적으로 묻는다면, 내 경험에 따르면 부드러운 게임을 얻으려면 가능한 한 GC를 최소화하고 싶습니다. – redtuna

1

이 코드를 볼 때 가장 먼저 떠오르는 것은 Direction이 String 필드 directionValue가 아니라 오히려 나침반 (Compass.EAST, Compass.WEST)을 저장하는 필드라는 것입니다.이렇게하면 MoveState.action()에서 문자열 비교를 제거 할 수 있으므로 코드를 훨씬 깔끔하게 정리해야합니다.

이름 지정에 문제가있는 것 같습니다. 아마도 NORTH, EAST, WEST 및 SOUTH가 Compass 대신 Direction이라는 열거 형에 있어야하며 현재 Direction 구현의 directionOnRight() 등이 해당되어야합니다. 정적 메서드 (현재 방향을 단일 인수로 가져오고 오른쪽/왼쪽/반대 방향을 반환)? 여분의 필드 IMHO에 저장할 필요가 없습니다 (조기 최적화에 관한 말을 기억하십시오 ;-).

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을 조사하고 방향에 대해 북/남/동/서쪽 Direction 열거 형을 사용합니다. (좌표) 위치는 내가 단순히 직면 열거에 deltaX()deltaY()을 구현하는 것이, X/Y 위치를 가지고 있으며, 이동 그런 다음 이동 코드는 단순히 같을 것이다

(그것은 칼 그냥 비슷한 게시 한 모양) :

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

당신이 직면하고있는 방향. 이 운동을 위임하지 않습니다. 누렁이는 항상 움직이지만, Direction 열거로 인해 dx/dy가 바뀝니다.

열거는 또한 NORTH.clockwise()를 호출하면 새 직면 값 EAST을 반환, 방법 clockwise()counterClockwise()을 가질 수 있습니다. 각 열거 인스턴스는 델타 시계 방향/반 시계 방향으로 방법이있을 것입니다, 당신의 Rover은 단순히 다음과 같습니다 훨씬 더 직관적이고 내가 기대했던 것 같다

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

. 나는 x와 y를 따로 표현했지만, 하나의 클래스로 감쌀 수있다. 그렇게하면 Direction 열거 형에서 이러한 객체를 처리해야하며 x 및 y로 다시 분리되지 않습니다.

0

나에게는 너무 복잡해 보입니다. 나는 이렇게해야한다고 생각합니다 : 당신의 로봇이 그의 선회 각도를 알도록하십시오. 그런 다음 왼쪽이나 오른쪽으로 돌리라고 요청하면이 각도를 바꿀 것입니다. 그가 움직일 것을 요청 받으면 그는이 각도에 따라 x, y 좌표로 움직일 것입니다. 각도는 나침반처럼 실제 각도 (0, 90, 180, 270)로 저장할 수 있습니다. sin (angle)과 cos (angle)에 이동 단계를 곱함으로써 로봇을 각도 방향으로 쉽게 이동시킬 수 있습니다. 어떤 단계 범위에서 t it be that simple? It will also handle more directions that just 4 and you을 움직일 수있는 이유는 무엇입니까?