TW Assignment的代碼實現

<span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);">這幾天練習設計模式,在網上看到了thoughtworks的一道Assignment,地址:</span><a target=_blank target="_blank" href="http://blog.sina.com.cn/s/blog_6ffe0cc10100yeon.html" style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);">點擊打開鏈接</a>


【原題】:

A squad of robotic rovers are to be landed by NASA on a plateau on Mars.

This plateau, which is curiously rectangular, must be navigated by the rovers so that their on-board cameras can get a complete view of the surrounding terrain to send back to Earth.

A rover's position and location is represented by a combination of x and y co-ordinates and a letter representing one of the four cardinal compass points. The plateau is divided up into a grid to simplify navigation. An example position might be 0, 0, N, which means the rover is in the bottom left corner and facing North.

In order to control a rover, NASA sends a simple string of letters. The possible letters are 'L ', 'R ' and 'M '. 'L ' and 'R ' makes the rover spin 90 degrees left or right respectively, without moving from its current spot.

'M ' means move forward one grid point, and maintain the same heading.

Assume that the square directly North from (x, y) is (x, y+1).

INPUT:

The first line of input is the upper-right coordinates of the plateau, the lower-left coordinates are assumed to be 0,0.

The rest of the input is information pertaining to the rovers that have been deployed. Each rover has two lines of input. The first line gives the rover 's position, and the second line is a series of instructions telling the rover how to explore the plateau.

The position is made up of two integers and a letter separated by spaces, corresponding to the x and y co-ordinates and the rover 's orientation.

Each rover will be finished sequentially, which means that the second rover won 't start to move until the first one has finished moving.

OUTPUT

The output for each rover should be its final co-ordinates and heading.

INPUT AND OUTPUT

Test Input:

5 5

1 2 N

LMLMLMLMM

3 3 E

MMRMMRMRRM

火星探測器

一小隊機器人探測器將由NASA送上火星高原,探測器將在這個奇特的矩形高原上行駛。

用它們攜帶的照相機將周圍的全景地勢圖發回到地球。每個探測器的方向和位置將由一個x,y系座標圖和一個表示地理方向的字母表示出來。爲了方便導航,平原將被劃分爲網格狀。位置座標示例:0,0,N,表示探測器在座標圖的左下角,且面朝北方。爲控制探測器,NASA會傳送一串簡單的字母。可能傳送的字母爲: 'L ', 'R '和 'M '。 'L ',和 'R '分別表示使探測器向左、向右旋轉90度,但不離開他所在地點。 'M ' 表示向前開進一個網格的距離,且保持方向不變。假設以廣場(高原)的直北方向爲y軸的指向。

輸入:首先輸入的line是座標圖的右上方,假定左下方頂點的座標爲0,0。剩下的要輸入的是被分佈好的探測器的信息。每個探測器需要輸入wo lines。第一條line 提供探測器的位置,第二條是關於這個探測器怎樣進行高原探測的一系列說明。位置是由兩個整數和一個區分方向的字母組成,對應了探測器的(x,y)座標和方向。每個探測器的移動將按序完成,即後一個探測器不能在前一個探測器完成移動之前開始移動。

輸出:每個探測器的輸出應該爲它行進到的最終位置座標和方向。輸入和輸出 測試如下:

期待的輸入:

5 5

1 2 N

LMLMLMLMM

3 3 E

MMRMMRMRRM 期待的輸出

1 3 N

5 1 E


【分析】

其實這種題目並不難,主要還是考實現者的問題實現方式


這是文件架構。

【實現】

一 : 方向包含東南西北四個狀態,於是我們先寫一個枚舉類型:

Direction.java

package main.com.thoughtworks.homework.state;

public enum Direction {
	NORTH,	//  北
	SOUTH,	//  南
	WEST,	//  西
	EAST  	//  東
}

二:涉及到不同方向狀態的切換,如果我們使用if - else實現對不同狀態的操作的話,不僅代碼難看,而且不容易維護,擴展難,使用狀態模式的方法來實現倒是不錯。

     2.1 定義一個State的抽象類

package main.com.thoughtworks.homework.state;


public abstract class State {
	int X;
	int Y;
	Direction direction;
	
	public State(int x, int y, Direction direction) {
		super();
		X = x;
		Y = y;
		this.direction = direction;
	}
	public int getX() {
		return X;
	}
	public void setX(int x) {
		X = x;
	}
	public int getY() {
		return Y;
	}
	public void setY(int y) {
		Y = y;
	}
	public Direction getDirection() {
		return direction;
	}
	public void setDirection(Direction direction) {
		this.direction = direction;
	}
	
	public abstract void turnLeft(Switcher s);
	
	public abstract void turnRight(Switcher s);
	
	public abstract void moveStep(Switcher s);
	
	@Override
	public String toString() {
		return "State [X=" + this.X + ", Y=" + this.Y + ", direction=" + this.direction + "]";
	}
	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + X;
		result = prime * result + Y;
		result = prime * result
				+ ((direction == null) ? 0 : direction.hashCode());
		return result;
	}
	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		State other = (State) obj;
		if (this.X != other.X)
			return false;
		if (this.Y != other.Y)
			return false;
		if (this.direction != other.direction)
			return false;
		return true;
	}	
}
需要注意的是State類中含有三個未實現的方法,turnLeft, turnRight, moveStep, 這三個方法將在具體的State子類中實現,並且僅僅作爲單一步驟的實現,例如turnLeft僅僅實現向左轉彎,但不前進,moveStep僅僅前進一步。

      2.2 定義一個狀態切換器Switcher,實現不同狀態的轉換

package main.com.thoughtworks.homework.state;

public class Switcher {
	State state;
	
	public Switcher(State state)
	{
		this.state = state;
	}
	public void setState(State state)
	{
		this.state = state;
	}
	
	public State getState()
	{
		return this.state;
	}
	
	public void turnLeft()
	{
		this.state.turnLeft(this);
	}
	
	public void turnRight()
	{
		this.state.turnRight(this);
	}
	
	public void moveStraight()
	{
		this.state.moveStep(this);
	}
}
 

      2.3 實現State的不同子類

        EastState.java

package main.com.thoughtworks.homework.state;

public class EastState extends State{

	public EastState(int x, int y, Direction direction) {
		super(x, y, direction);
	}

	@Override
	public void turnLeft(Switcher s) {
		if(s.getState().getDirection() == Direction.EAST)
		{
			s.setState(new NorthState(this.X, this.Y, Direction.NORTH));
		}
		else
		{
			s.setState(new SouthState(this.X, this.Y, this.direction));
			s.turnLeft();
		}
	}

	@Override
	public void turnRight(Switcher s) {
		if(s.getState().getDirection() == Direction.EAST)
		{
			s.setState(new NorthState(this.X, this.Y, Direction.SOUTH));
		}
		else
		{
			s.setState(new SouthState(this.X, this.Y, this.direction));
			s.turnRight();
		}
	}

	@Override
	public void moveStep(Switcher s) {
		if(s.getState().getDirection() == Direction.EAST)
		{
			this.X += 1;
			s.setState(this);
		}
		else
		{
			s.setState(new SouthState(this.X, this.Y, this.direction));
			s.moveStraight();
		}
	}

}
       

      SouthState.java

package main.com.thoughtworks.homework.state;

public class SouthState extends State{

	public SouthState(int x, int y, Direction direction) {
		super(x, y, direction);
	}

	@Override
	public void turnLeft(Switcher s) {
		if(s.getState().getDirection() == Direction.SOUTH)
		{
			s.setState(new EastState(this.X, this.Y, Direction.EAST));
		}
		else
		{
			s.setState(new WestState(X, Y, direction));
			s.turnLeft();
		}
	}

	@Override
	public void turnRight(Switcher s) {
		if(s.getState().getDirection() == Direction.SOUTH)
		{
			s.setState(new WestState(this.X, this.Y, Direction.WEST));
		}
		else
		{
			s.setState(new WestState(X, Y, direction));
			s.turnRight();
		}
	}

	@Override
	public void moveStep(Switcher s) {
		if(s.getState().getDirection() == Direction.SOUTH)
		{
			this.Y -= 1;
			s.setState(this);
		}
		else
		{
			s.setState(new WestState(X, Y, direction));
			s.moveStraight();
		}
	}
}
   另外的WestState和NorthState實現均類似。

三,  執行器的實現

     執行器就是要執行命令的對象,我們定義爲Executer

    Executer.java

package main.com.thoughtworks.homework.executer;

import main.com.thoughtworks.homework.state.Switcher;

public class Executer {
	protected Switcher switcher;
	
	public Executer(Switcher switcher) {
		super();
		this.switcher = switcher;
	}

	public Switcher getSwitcher() {
		return switcher;
	}

	public void setSwitcher(Switcher switcher) {
		this.switcher = switcher;
	}

	public void turnLeft()
	{
		this.switcher.turnLeft();
		this.switcher.moveStraight();
	}
	
	public void turnRight()
	{
		this.switcher.turnRight();
		this.switcher.moveStraight();
	}
	
	public void moveStraight()
	{
		this.switcher.moveStraight();
	}
}
 

      Executer包含了一個狀態切換器,用來執行命令時,可以改變狀態

 

四、  定義一個命令接口IExecutable,包含一個必須實現的execute方法

     IExecutable.java


package main.com.thoughtworks.homework.executer;

public interface IExecutable {
	void execute(Executer e);
}

   編寫向左、向右、前進這幾個命令,實現這個接口

package main.com.thoughtworks.homework.executer;


public class LeftCommand implements IExecutable{

	@Override
	public void execute(Executer e) {
		e.turnLeft();
	}

}

package main.com.thoughtworks.homework.executer;


public class RightCommand implements IExecutable{

	@Override
	public void execute(Executer e) {
		e.turnRight();
	}
	
}

package main.com.thoughtworks.homework.executer;


public class StraightCommand implements IExecutable{
	@Override
	public void execute(Executer e) {
		e.moveStraight();
	}

}

五.  主函數調用

package main.com.thoughtworks.homework.main;

import main.com.thoughtworks.homework.executer.*;
import main.com.thoughtworks.homework.state.*;

public class Main {
	public static void main(String[] args)
	{
		Switcher s = new Switcher(new NorthState(2, 4, Direction.SOUTH));
		Executer rover = new Executer(s);
		new LeftCommand().execute(rover);
		
		System.out.println(rover.getSwitcher().getState().toString());
	}
}	

六、可以用靜態工廠模式實現初始的時候,對狀態State進行實例化工作,在此不貼代碼了。另外最後的輸出也還沒寫進來,相信你們都會的!


七、如果這時候我們想擴展一個命令,叫D,執行器接收到這個命令時,會先左轉前進一個單元,然後右轉前進一個單元,那麼我們可以定義一個ExecuterX繼承自Executer,添加一個方法

package main.com.thoughtworks.homework.extension;

import main.com.thoughtworks.homework.executer.Executer;
import main.com.thoughtworks.homework.state.Switcher;

public class ExecuterX extends Executer{

	public ExecuterX(Switcher switcher) {
		super(switcher);
		
	}

	public void exMethod()
	{
		this.switcher.turnLeft();
		this.switcher.moveStraight();
                this.switcher.turnRight();
		this.switcher.moveStraight();
	}
}

再定義一個D命令,實現IExecutable接口
package main.com.thoughtworks.homework.extension;

import main.com.thoughtworks.homework.executer.Executer;
import main.com.thoughtworks.homework.executer.IExecutable;

public class DCommand implements IExecutable{

	@Override
	public void execute(Executer e) {
		if(e instanceof ExecuterX)
		{
			ExecuterX ex = (ExecuterX)e;
			ex.exMethod();
		}
	}

}

  
如果各位看官有什麼意見或想法,歡迎留言,或者發送郵件到 anrial#126.com。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章