状态模式的应用

看了2天文章,还是搞不太明白如何应用状态模式
例如:考生有3种状态:等待考试,正在考试,已交卷

每个状态的变化都需要一些跟状态本身无关的额外操作,例如更新考试时间,计算成绩,写入数据库等;而这些操作是否执行,取决于状态是否转换成功

考生Student

假设State接口有如下两个方法
startExam(Student stu)
submitExam(Student stu)

有3个具体的State:StateBefore,StateIng,StateSubmit

那么StateIng
void submitExam(Student stu){
stu.setState(new StateSubmit(stu));
}
假设此时还应该调用Student的calScore操作,这个操作应该放到什么地方呢?如果放到此方法里,State和Student的耦合度是不是太高了,而且似乎State本身应该只关心当前状态和下一个状态,对Student的其他数据不应该关心,对么?

不知道我描述清楚了没有

你的State接口有两个方法startExam(Student stu)和submitExam(Student stu)抽象得不精确。

state接口应该是两个基本方法:获得当前状态;获得到下一个状态(包括转换规则)

不太明白,怎么会只有2个基本方法呢,方法的个数应该取决于业务逻辑

public interface State {

public void startExam(Student stu) throws ExamException;

public void submitExam(Student stu) throws ExamException;

}
//-----------------------------------
public class Student {
private State state;

public void calScore() {
// 计算成绩
}

public void setState(State state) {

}
}
//--------------------------------------
public class StateNotStart implements State {

public void startExam(Student stu) throws ExamException {
synchronized (stu) {
stu.setState(new StateExaming());
}
}

public void submitExam(Student stu) throws ExamException {
throw new ExamException("Wrong State");
}

}
//----------------------------------------
public class StateExaming implements State {

public void startExam(Student stu) throws ExamException {
throw new ExamException("Wrong State");
}

public void submitExam(Student stu) throws ExamException {
synchronized (stu) {
stu.calScore();
stu.setState(new StateFinished());
}
}

}
//-------------------------------
public class StateFinished implements State {

public void startExam(Student stu) throws ExamException {
throw new ExamException("Wrong State");
}

public void submitExam(Student stu) throws ExamException {
throw new ExamException("Wrong State");
}

}

还是State和Student耦合,不过觉得就应该这样的说,期待banq指教

>方法的个数应该取决于业务逻辑
这个认识我认为有问题的,状态模式作为一个专门设计模式,它是和业务不太有关系的模式,它是为专门解决状态这个核心问题而产生的模式,至于业务逻辑可以独立到外面和状态模式协同合作。

这里面有一个细分问题,要将业务逻辑中的状态state控制分离出来移植到状态模式中实现。

banq大师,能不能说得具体点,具体到这个例子该如何设计?

比如现在状态要增加一个“被取消考试资格”,相对应的业务逻辑是被取消考试资格,我的理解是State接口要多一个cancelExam方法,所有的State实现都应对该方法作处理。这就是我说得方法的个数取决于业务逻辑

真的不知道该如何:业务逻辑可以独立到外面和状态模式协同合作

还有你说得:状态模式作为一个专门设计模式,它是和业务不太有关系的模式,那要他还有什么意义呢?不解阿

谢谢

不要太客气,其实我已经在前面说了:
state接口应该是两个基本方法:获得当前状态;获得到下一个状态

你可以按照我的文章:
http://www.jdon.com/designpatterns/designpattern_State.htm
及其文章后面的英文源码案例来实现,基本是我上面说的两个方法。

只有你一个人给我解惑,客气是应该的,呵呵....

1. 我看了您的那篇文章,首先说:
state接口应该是两个基本方法:获得当前状态;获得到下一个状态

但是那个例子的State
public abstract class State{
  public abstract void handlepush(Context c);
  public abstract void handlepull(Context c);
  public abstract void getcolor();
}
其中handlepush和handlepull都是和业务相关的,如果需要增加doublePush这种操作,接口也要增加方法:handleDoublePush,对么?

2.还是这篇文章中,那个Sample是做什么的?和Context什么关系?我理解的State是Context的State,Context还有其他数据,需要在State转换中进行处理。甚至State的转换规则有可能还要依赖Context的其他属性,这样是不是就属于抽象的不彻底?

3.How to implement state-dependent behavior这篇文章中
abstract class State {
static State initialState;
static final State state1 = new State1();
static final State state2 = new State2();
protected State() {
if (initialState == null) initialState = this;
}
abstract void stateExit(StateOwner owner);
abstract void stateEnter(StateOwner owner);
}

你说的:获得当前状态;获得到下一个状态,难道是指这个?应该不是吧,怎么会只有“获得到下一个状态”这样的方法呢?一个State的下一个State不是固定的阿,例如考生可以:正在考试 --> 交卷,正在考试 --> 被取消考试资格,正在考试 --> 重新登陆


public abstract class State{
  public abstract void handlepush(Context c);
  public abstract void handlepull(Context c);
  public abstract void getcolor();
}

上述代码中确实是包含两个基本方法:
1. 封装状态转换规则(转换到下一个状态规则)
2. 当前状态
handlepush和handlepull封装的是转换到上一个或下一个状态,叶就是封装状态转换规则。
getColor是获得当前状态。

关于老外那个例子,也差不多:
stateExit和stateEnter表示状态进入或退出,这是StateOwner 这个主动对象完成的。

重点是:围绕当前状态,把当前状态前后转换规则封装起来,打个比喻:幼儿园小孩排队,每个小孩(状态)只要记住自己前面一个和后面一个是谁就可以了,这是一个最简单有效的处理方式,当有新的小朋友(新的状态)插入时,它只要指定插入哪个小朋友后面就可以,而原来跟在那个小朋友之后的小朋友只要更改为跟在这个新小朋友后面就可以,这样新状态加入只涉及原来状态链中一个状态的修改,减少因为状态动态变化导致的修改范围。

为什么不再添加一个已经计算得分的状态呢