我所在的公司是一家生产奢侈品眼镜的生产型企业,产品的主数据由PLM管理,一副眼镜从设计到出库需要经历如下的生命周期:
- Preliminary: 设计的初始化,指我们开始着手设计新的产品型号
- Export: 设计的主数据开始可以导出到SAP系统来让SAP做数据的增强
- Review: 产品的设计及主数据的管理在此状态需要得到相关部门的审核
- Release: 产品设计审核通过,数据发送到SAP指导生产
- Cancelled: 该产品的款式已经过时,需要把设计废弃掉(有些型号永远都不会出现这种情况,例如0RB3025,永恒的经典)
我们可以看到,这5个状态描述了一个特定型号在不同的生命周期它所属的状态,注意状态的转变是线性的,你无法从Preliminary直接跳到Review状态。
产品在不同的阶段可以做不同的事情,如果当前状态是Preliminary而你试图去操作Release状态的数据,系统就会告诉你不能这样做。那么系统是如何做到的?作为java程序员第一时间想到的肯定是简单而快速的解决方案:if...else..., 于是我们就有了如下的代码:
大家知道如此之多的if...else...代码肯定是难以维护的,试想一下如果我们要在Release状态之后和Cancelled状态之前新增一个Suspend状态我们该怎么做?我们不得不去修改这一大堆if...else代码来让每一个状态认知这个新状态并做出相应的反应,这明显不是一个有弹性的设计,我们是否可以借助于某种设计模式来解决目前这种难题呢?
答案是肯定的,我们可以使用状态模式来达到状态跟上下文(Context)松耦合的目的,状态模式的定义是这样的:
我先用UML类图来描述一下状态模式的大致架构:
希望大家不要被这个大类图给吓到,其实理解起来很容易的:
- 有一个State接口,该接口定义了每个状态下能做的事情(方法)。
- 每一个状态都有一个具体的类实现了状态接口,特别注意实现类里面持有Context类的引用,用来根据不同的状态来改变Context类的当前状态。
- 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的当前状态从而让它做不同的事情,而客户端对此浑然不觉;策略模式则不一样,它封装了一群算法并且让各个算法实现之间可以相互替换,客户依据业务逻辑决定需要使用哪一个真正的算法。状态模式也是有缺点的,因为为了达到一个类一个责任的目的,每个状态都是一个类,我们需要增加很多类来实现这种模式,优点就是这样可以有效得做到松散耦合,从而让整个设计对修改关闭对扩展开放,具体是否采用这种模式需要各位自己权衡了。