一 问题描述
有一个N*N的矩形,现在从四个顶角的一个位置出发,按照下图的方式走完该矩形,输出经过的坐标。
二 分析
如何在实际应用中运用State模式呢?
从上面的图中我们可以得到这样的描述:从左上角开始,向东走,到第一行结束的时候,改变方向向南,走到下一行,然后向西走,到第二行开始的时候,改变方向向南,在走到第三行,改变方向朝东…。考虑到另外三个开始的方位,我们可以得到:
状态:朝东,朝西,朝南,朝北
事件:左转,右转,前行
(注意:如果要用设计模式来解决问题,千万不要从编码实现的角度来考虑如何解决这个问题,尝试着从比较抽象的角度来考虑如何解决这个问题。)
状态图
根据状态图不使用State模式的可能的代码:
//假设xmax×ymax的矩形,那么矩阵的遍历可以这样实现
while (y<=yMax||x<=xMax) { forward(); }
public void forward(){
switch(dirState){
case E: if (x <xmax) x++; else dirState.right(); break;
case S: if (x==xmax)
dirState.right();
else dirState.left(); break;
case W: if (x>xmin) x--;
else dirState.left(); break;
case N: dirState.right();
}
}
使用STATE模式 类图:
说明:Context为状态机,持有一个状态对象(SAState),提供改变状态的事件run() right() left()。在本例种,Context状态机初始状态为East状态,用run()来触发状态发生改变,状态具体如何改变,由实现SAState接口的各个子状态实现。如:状态机的初始态为East,East的run()动作在到达第一行尾后,执行right(),使状态机的状态由East改尾South。
三 实现
SAState.java
interface SAState {
public void run(Context c);
public void left(Context c);
public void right(Context c);
}
SAStateEast.java
public class SAStateEast implements SAState {
public SAStateEast() {
}
public void left(Context c) {
c.y--;
c.setState(new SAStateNorth());
}
public void right(Context c) {
c.y++;
c.setState(new SAStateSouth());
}
public void run(Context c) {
if (c.x < c.xMax) {
c.x++;
} else {
c.right();
}
}
}
SAStateSouth.java
public class SAStateSouth implements SAState {
public SAStateSouth() {
}
public void left(Context c) {
c.setState(new SAStateEast());
}
public void right(Context c) {
c.setState(new SAStateWest());
}
public void run(Context c) {
if (c.x == c.xMax) {
c.right();
} else {
c.left();
}
}
}
SAStateWest.java
public class SAStateWest implements SAState {
public SAStateWest() {
}
public void left(Context c) {
c.y++;
c.setState(new SAStateSouth());
}
public void right(Context c) {
c.y++;
c.setState(new SAStateNorth());
}
public void run(Context c) {
if (c.x > c.xMin) {
c.x--;
} else {
c.left();
}
}
}
SAStateNorth.java
public class SAStateNorth implements SAState {
public SAStateNorth() {
}
public void left(Context c) {
c.y--;
c.setState(new SAStateWest());
}
public void right(Context c) {
c.y++;
c.setState(new SAStateEast());
}
public void run(Context c) {
c.right();
}
}
Context.java
public class Context {
//变量初始化
int xMax;
int yMax;
int xMin = 1;
int yMin = 1;
int x;
int y = 1;
//初始化状态
SAState thisState;
//外部调用函数
public void sweep(int inXMax, int inYMax) {
xMax = inXMax;
yMax = inYMax;
setState(new SAStateEast());
while (y < yMax || x < xMax) {
this.run();
System.out.println("(x=" + x + ",y=" + y + ")\n");
}
}
//功能函数
void setState(SAState state) {
this.thisState = state;
}
public void run() {
thisState.run(this);
}
public void right() {
thisState.right(this);
}
public void left() {
thisState.left(this);
}
}
四 总结
我们可以看到使用STATE模式将那些再Switch种的每一个分支放入一个独立的类中,使得分支可以独立的变化。而在状态机中则只要提供给外界一个初始状态何一个事件的调用函数就可以了,而不用去关注状态之间时如何转换的。
记得在学习设计模式的时候由一本书上用星际争霸里面的机枪兵拿来做STATE模式的例子:机枪兵有(1)普通状态(2)打兴奋剂的状态(3)有医务人员在周围的状态,不同的状态下发的子弹数目和被子弹击毙的数目不同。呵呵,很是经典。
最近在写一个计算器玩的时候,发现计算器中也可以用状态模式来实现,在下篇文章中用C++写着看看。
(注:文章中的代码只考虑到了起始在左上的情况,而且总觉得有点别扭,如果有问题,还忘大家指出,谢谢:))