zoukankan      html  css  js  c++  java
  • 设计模式学习笔记之十:状态模式

    我所在的公司是一家生产奢侈品眼镜的生产型企业,产品的主数据由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的当前状态从而让它做不同的事情,而客户端对此浑然不觉;策略模式则不一样,它封装了一群算法并且让各个算法实现之间可以相互替换,客户依据业务逻辑决定需要使用哪一个真正的算法。状态模式也是有缺点的,因为为了达到一个类一个责任的目的,每个状态都是一个类,我们需要增加很多类来实现这种模式,优点就是这样可以有效得做到松散耦合,从而让整个设计对修改关闭对扩展开放,具体是否采用这种模式需要各位自己权衡了。

  • 相关阅读:
    Spring Security 记住我功能 详解
    浅谈前端SPA(单页面应用)
    Token问什么可以避免CSRF/XSRF?
    总结 XSS 与 CSRF 两种跨站攻击
    localStorage,sessionStorage和cookie的区别及使用
    cookie,token验证的区别
    彻底弄懂session,cookie,token
    HTTP cookies 详解
    纯css3实现文字间歇滚动效果
    我的less学习之路
  • 原文地址:https://www.cnblogs.com/stonefeng/p/5785623.html
Copyright © 2011-2022 走看看