设计模式学习笔记之十:状态模式

我所在的公司是一家生产奢侈品眼镜的生产型企业,产品的主数据由PLM管理,一副眼镜从设计到出库需要经历如下的生命周期:

  1. Preliminary: 设计的初始化,指我们开始着手设计新的产品型号
  2. Export: 设计的主数据开始可以导出到SAP系统来让SAP做数据的增强
  3. Review: 产品的设计及主数据的管理在此状态需要得到相关部门的审核
  4. Release: 产品设计审核通过,数据发送到SAP指导生产
  5. Cancelled: 该产品的款式已经过时,需要把设计废弃掉(有些型号永远都不会出现这种情况,例如0RB3025,永恒的经典)

我们可以看到,这5个状态描述了一个特定型号在不同的生命周期它所属的状态,注意状态的转变是线性的,你无法从Preliminary直接跳到Review状态。
产品在不同的阶段可以做不同的事情,如果当前状态是Preliminary而你试图去操作Release状态的数据,系统就会告诉你不能这样做。那么系统是如何做到的?作为java程序员第一时间想到的肯定是简单而快速的解决方案:if...else..., 于是我们就有了如下的代码:

大家知道如此之多的if...else...代码肯定是难以维护的,试想一下如果我们要在Release状态之后和Cancelled状态之前新增一个Suspend状态我们该怎么做?我们不得不去修改这一大堆if...else代码来让每一个状态认知这个新状态并做出相应的反应,这明显不是一个有弹性的设计,我们是否可以借助于某种设计模式来解决目前这种难题呢?
答案是肯定的,我们可以使用状态模式来达到状态跟上下文(Context)松耦合的目的,状态模式的定义是这样的:

我先用UML类图来描述一下状态模式的大致架构:

希望大家不要被这个大类图给吓到,其实理解起来很容易的:

  1. 有一个State接口,该接口定义了每个状态下能做的事情(方法)。
  2. 每一个状态都有一个具体的类实现了状态接口,特别注意实现类里面持有Context类的引用,用来根据不同的状态来改变Context类的当前状态。
  3. Context类(Eyewear)持有每个状态实现的引用

我们再来看看代码:
State接口
public interface State {

public void doPreliminary();
public void doExport();
public void doReview();
public void doRelease();
public void doCancelled();

}

各个状态实现类:
public class PreliminaryState implements State {

private Eyewear eyewear;

public PreliminaryState(Eyewear eyewear) {
	this.eyewear = eyewear;
}

@Override
public void doPreliminary() {
	System.out.println("Preliminary: We are planning to make a new glasses.");
	eyewear.setState(eyewear.getExportState());
}

@Override
public void doExport() {
	System.out.println("PreliminaryState: Need initialize a prototype before export to SAP");
}

@Override
public void doReview() {
	System.out.println("PreliminaryState: Need export to SAP before review the details");
}

@Override
public void doRelease() {
	System.out.println("PreliminaryState: Need approve the details before release the model");
}

@Override
public void doCancelled() {
	System.out.println("PreliminaryState: Need release the model before cancel it");
}

}

public class ExportState implements State {

private Eyewear eyewear;

public ExportState(Eyewear eyewear) {
	this.eyewear = eyewear;
}	

@Override
public void doPreliminary() {
	System.out.println("ExportState: You already initialized that model");
}

@Override
public void doExport() {
	System.out.println("Export: Sent to SAP as master data");
	eyewear.setState(eyewear.getReviewState());
}

@Override
public void doReview() {
	System.out.println("ExportState: Need export to SAP before review the details");
}

@Override
public void doRelease() {
	System.out.println("ExportState: Need approve the details before release the model");
}

@Override
public void doCancelled() {
	System.out.println("ExportState: Need release the model before cancel it");
}

}

注意PreliminaryState类在做完doPreliminary事情后会把Eyewear的当前状态改为ExportState,这样doExport的时候就能调用到正确的状态实现类的方法,由于ReviewState,ReleaseState和CancelledState的实现大同小异,在这里我就不一一赘述了。

Eyewear(Context类)

public class Eyewear {

private State preliminaryState;
private State exportState;
private State reviewState;
private State releaseState;
private State cancelledState;
private State currentState;

public Eyewear() {
	preliminaryState = new PreliminaryState(this);
	exportState = new ExportState(this);
	reviewState = new ReviewState(this);
	releaseState = new ReleaseState(this);
	cancelledState = new CancelledState(this);
	// initialized state is preliminary
	currentState = preliminaryState;
}

public State getPreliminaryState() {
	return preliminaryState;
}

public State getExportState() {
	return exportState;
}

public State getReviewState() {
	return reviewState;
}

public State getReleaseState() {
	return releaseState;
}

public State getCancelledState() {
	return cancelledState;
}

public void setState(State state) {
	this.currentState = state;
}

public void doPreliminary() {
	currentState.doPreliminary();
}

public void doExport() {
	currentState.doExport();
}

public void doReview() {
	currentState.doReview();
}

public void doRelease() {
	currentState.doRelease();
}

public void doCancelled() {
	currentState.doCancelled();
}

}

可以看到Eyewear在初始化的时候就是Preliminary状态并在构造方法里面初始化了所有状态实现类的实例。

测试方法及结果:
public class MyTest {

@Test
public void test() {
	Eyewear eyewear = new Eyewear();
	eyewear.doPreliminary();
	eyewear.doExport();
	eyewear.doReview();
	eyewear.doRelease();
	eyewear.doCancelled();
}

}

因为状态的promote是线性的不能跳级,如果我把eyewear.doPreliminary()这行代码给注释起来,你会发现状态一直停留在PreliminaryState上,程序运行也不会得到正确的结果并且会通知用户:

总结

状态模式其实跟策略模式很像,只不过这两种模式的意图不同,状态模式重点在于:根据业务逻辑的需要,Context类跟状态类都可以改变Context的当前状态从而让它做不同的事情,而客户端对此浑然不觉;策略模式则不一样,它封装了一群算法并且让各个算法实现之间可以相互替换,客户依据业务逻辑决定需要使用哪一个真正的算法。状态模式也是有缺点的,因为为了达到一个类一个责任的目的,每个状态都是一个类,我们需要增加很多类来实现这种模式,优点就是这样可以有效得做到松散耦合,从而让整个设计对修改关闭对扩展开放,具体是否采用这种模式需要各位自己权衡了。

posted @ 2016-08-18 22:33  StoneFeng  阅读(380)  评论(0编辑  收藏  举报