Java设计模式--状态模式【State Pattern】

本文通过打印机工作流程示例,介绍状态模式的设计思想与实现方法,对比策略模式,解析状态模式的应用场景。
         在办公室,经常要打印文件。考虑一下打印机的工作原理:它主要有无纸状态、有纸状态、打印状态、停止状态。当按下打印按钮,打印机开始打印,如果发现没有纸,应该让打印机停下来,放入纸张后,才能继续打印,打印完毕,打印机停止。

  实现这样的功能,首先想到的代码结构是这样的:定义一个打印机的接口,接口中定义打印机的各个状态和方法,代码如下:

package com.pattern.state.v1;
/**
 * 打印机抽象接口
 * @author 
 *
 */
public interface IPrinter {
  public final static int STATE_PRINT=1;//开始打印
  public final static int STATE_NOPAPER=2;//没有纸
  public final static int STATE_HAVEPAPER=3;//已有纸张
  public final static int STATE_STOP=4;//停止打印
  
  public void setState(int state);
  
  public void print();
  public void nopaper();
  public void havepaper();
  public void stop();
}

然后定义具体实现类,利用if...else... 或者switch条件语句,根据不同的状态去执行不同的方法:

package com.pattern.state.v1;
/**
 * 打印机实现类
 * @author 
 *
 */
public class Printer implements IPrinter {
  private int state=4;
  
  @Override
	public void setState(int state) {
		this.state=state;
	}
  
	@Override
	public void print() {//什么情况下开始打印
		switch(this.state){
	       case STATE_PRINT://如果正在打印,则什么都不做
	    	   break;
	       case STATE_NOPAPER://如果没有纸,则什么都不做
	    	   break;
	       case STATE_HAVEPAPER://如果有纸了,则开始打印
	    	   this.printer_print();
	    	   this.setState(STATE_PRINT);
	    	   break;
	       case STATE_STOP://如果打印机是停止状态,则开始打印
	    	   this.printer_print();
	    	   this.setState(STATE_PRINT);
	    	   break;
    }

	}

	@Override
	public void stop() {//什么情况下让打印机停止
		switch(this.state){
	       case STATE_PRINT://如果正在打印,则让打印机停止
	    	   this.printer_stop();
	    	   this.setState(STATE_STOP);
	    	   break;
	       case STATE_NOPAPER://如果没有纸,则打印机本来就是停止的,什么都不做
	    	   break;
	       case STATE_HAVEPAPER://如果有纸,让打印机停止
	    	   this.printer_stop();
	    	   this.setState(STATE_STOP);
	    	   break;
	       case STATE_STOP://若果打印机是停止状态,则什么都不做
	    	   break;
       }
	}

	
	 @Override
	  public void nopaper() {//什么情况下没纸
		 switch(this.state){
	       case STATE_PRINT://打印机正在打印,打印机没纸,打印机停止
	    	   this.printer_nopaper();
	    	   this.setState(STATE_NOPAPER);
	    	   this.printer_stop();
	    	   this.setState(STATE_STOP);
	    	   break;
	       case STATE_NOPAPER://如果没有纸,则什么都不做
	    	   break;
	       case STATE_HAVEPAPER://有纸了,则让打印机没纸
	    	   this.printer_nopaper();
	    	   this.setState(STATE_NOPAPER);
	    	   break;
	       case STATE_STOP://打印机停止状态,则让打印机没纸
	    	   this.printer_nopaper();
	    	   this.setState(STATE_NOPAPER);
	    	   break;
}	
	  	
	  }

	@Override
	public void havepaper() {//什么情况下可以让打印机有纸
		switch(this.state){
	       case STATE_PRINT://打印机正在打印,则什么都不做
	    	   break;
	       case STATE_NOPAPER://如果没有纸,则放入纸
	    	   this.printer_havepaper();
	    	   this.setState(STATE_HAVEPAPER);
	    	   break;
	       case STATE_HAVEPAPER://有纸了,则什么都不做
	    	   break;
	       case STATE_STOP://打印机停止状态,则放入纸张
	    	   this.printer_havepaper();
	    	   this.setState(STATE_HAVEPAPER);
	    	   break;
  }	
	}
	
  private void printer_havepaper(){
	  System.out.println("打印机被放入纸张,有纸了...");
  }
  private void printer_stop(){
	  System.out.println("打印机停止...");
  }
  private void printer_print(){
	  System.out.println("打印机开始打印...");
  }	
  private void printer_nopaper(){
	  System.out.println("打印机没纸了...");
  }
}
客户端测试类:

package com.pattern.state.v1;
/**
 * 客户端测试:测试打印机一个完整的打印周期
 * @author 
 *
 */
public class Client {

	public static void main(String[] args) {
		IPrinter p=new Printer();
		p.print();//打印
		p.nopaper();//打印机没纸
		p.stop();//停止
		p.havepaper();//放纸
		p.print();//开始打印
        p.stop();//打印完毕,停止
	}
}
结果输出:


上面的代码能够实现基本功能,但是会有如下缺点:

   1、随着”状态“的增多,会产生大量的if...else或者switch...语句, 代码扩展性极差

   2、没有遵守"开放-关闭"原则

   3、不符合面向对象思想

   4、状态转换隐藏在条件语句中,任意改动都需要改变条件判断,容易导致BUG

  那么,我们将如何改变程序结构呢?目前我们是通过行为来改变状态,为什么不能用状态来改变其行为呢?这就是我们今天的主题--状态模式。因为状态模式将状态封装成为独立的类,并将动作委托到代表当前状态的对象,行为就会随着内部状态而改变。

   什么是状态模式呢?所谓状态模式就是当一个对象内在状态改变时允许其改变行为,这个对象看起来像是改变了其类。就是说状态模式封装的非常好,状态的变更引起了行为的变更,从外部看起来就好像这个对象对应的类发生了改变一样。

      状态模式的通用类图如下:

     

从上图可以看出,状态模式所涉及到的角色有:

  环境(Context)角色,也称上下文:定义客户端所感兴趣的接口,并且保留一个具体状态类的实例。这个具体状态类的实例给出此环境对象的现有状态。

  抽象状态(State)角色:定义一个接口,用以封装环境(Context)对象的一个特定的状态所对应的行为。

  具体状态(ConcreteState)角色:每一个具体状态类都实现了环境(Context)的一个状态所对应的行为。

用状态模式实现打印机功能。代码如下:

首先定义抽象状态接口:

package com.pattern.state.v2;
/**
 * 打印机状态接口
 * @author 
 *
 */
public interface IPrinterState {
	public void nopaper();
    public void havepaper();
    public void print();
    public void stop();
}
打印状态实现类:

package com.pattern.state.v2;
/**
 * 打印机--打印状态
 * @author 
 *
 */
public class PrintState implements IPrinterState {
	private PrinterManager manager;
	public PrintState(PrinterManager manager) {
		super();
		this.manager = manager;
	}

	@Override
	public void havepaper() {
		System.out.println("打印机正在打印,已经有纸...");
	}

	@Override
	public void print() {
		System.out.println("打印机开始打印...");
	}

	@Override
	public void stop() {
		System.out.println("打印机停止...");
		this.manager.setState(this.manager.getStopState());

	}
	@Override
	public void nopaper() {
		System.out.println("打印机无纸...");
	}
    
}
无纸状态实现类:

package com.pattern.state.v2;
/**
 * 打印机--无纸状态
 * @author 
 *
 */
public class NopaperState implements IPrinterState {
   private PrinterManager manager;
   
	public NopaperState(PrinterManager manager) {
	super();
	this.manager = manager;
}

	@Override
	public void havepaper() {
     System.out.println("打印机被放入纸张...");
     this.manager.setState(this.manager.getPutpaperState());
	}

	@Override
	public void print() {
		System.out.println("打印机无纸了,先停下放纸,再打印...");
		this.manager.setState(this.manager.getStopState());
		this.manager.setState(this.manager.getPutpaperState());
		this.manager.setState(this.manager.getPrintState());
	}

	@Override
	public void stop() {
		System.out.println("打印机停止...");
		this.manager.setState(this.manager.getStopState());
	}

	@Override
	public void nopaper() {
		System.out.println("打印机已经无纸...");
	}

}
有纸状态实现类:

package com.pattern.state.v2;
/**
 * 打印机--有纸状态
 * @author 
 *
 */
public class HavepaperState implements IPrinterState {
   private PrinterManager manager;
   
	public HavepaperState(PrinterManager manager) {
	super();
	this.manager = manager;
}

	@Override
	public void havepaper() {
     System.out.println("打印机已经有纸...");
	}

	@Override
	public void print() {
		System.out.println("打印机开始打印...");
		this.manager.setState(this.manager.getPrintState());
	}

	@Override
	public void stop() {
		System.out.println("打印机停止...");
		this.manager.setState(this.manager.getStopState());
	}

	@Override
	public void nopaper() {
		 System.out.println("让打印机无纸...");
		 this.manager.setState(this.manager.getNopaperState());
		
	}

}
停止状态实现类:

package com.pattern.state.v2;
/**
 * 打印机--停止状态
 * @author 
 *
 */
public class StopState implements IPrinterState {
	private PrinterManager manager;
	public StopState(PrinterManager manager) {
		super();
		this.manager = manager;
	}

	@Override
	public void havepaper() {
		System.out.println("打印机放入纸张...");
		this.manager.setState(this.manager.getPutpaperState());
	}

	@Override
	public void print() {
		System.out.println("打印机开始打印...");
		this.manager.setState(this.manager.getPrintState());

	}

	@Override
	public void stop() {
		System.out.println("打印机已经是停止状态...");
	}

	@Override
	public void nopaper() {
		System.out.println("让打印机无纸...");
		this.manager.setState(this.manager.getNopaperState());
		
	}

}
打印机状态管理系统,即环境上下文角色:

package com.pattern.state.v2;
/**
 * 打印机状态管理系统:让打印机在不同的状态之间切换
 * @author 
 *
 */
public class PrinterStateManager {
	private IPrinterState nopaperState;//无纸状态
	private IPrinterState havepaperState;//有纸状态
	private IPrinterState printState;//打印状态
	private IPrinterState stopState;//停止状态
	
	private IPrinterState state=stopState;//默认停止状态
	private boolean isOpen=false;//打印开关  打印?不打印?默认不打印

	public PrinterStateManager(boolean pressOpen) {
		nopaperState=new NopaperState(this);
		havepaperState=new HavepaperState(this);
		printState=new PrintState(this);
		stopState=new StopState(this);
		setIsOpen(pressOpen);
		if(pressOpen)this.setState(printState);//若打开打印开关,则开始打印
	}
	
	public void setState(IPrinterState state){
		this.state=state;
	}

	public IPrinterState getPutpaperState() {
		return havepaperState;
	}

	public IPrinterState getPrintState() {
		return printState;
	}

	public IPrinterState getNopaperState() {
		return nopaperState;
	}

	public IPrinterState getStopState() {
		return stopState;
	}
	
	public void putpaper(){
		this.state.havepaper();
	}
	public void print(){
		this.state.print();
	}
	public void stop(){
		this.state.stop();
	}
	public void nopaper(){
		this.state.nopaper();
	}
	public void setIsOpen(boolean isOpen) {
		this.isOpen = isOpen;
	}
}
客户端测试类:

package com.pattern.state.v2;
/**
 * 客户端测试:一个完整的打印周期
 * @author 
 *
 */
public class Client {
	public static void main(String[] args) {
		PrinterStateManager manager=new PrinterStateManager(true);
		manager.print();
		manager.nopaper();
		manager.stop();
		manager.putpaper();
		manager.print();
		manager.stop();
	}
}
结果输出:


      在状态模式中,环境(Context)是持有状态的对象,但是环境(Context)自身并不处理跟状态相关的行为,而是把处理状态的功能委托给了状态对应的状态处理类来处理。

  在具体的状态处理类中经常需要获取环境(Context)自身的数据,甚至在必要的时候会回调环境(Context)的方法,因此,通常将环境(Context)自身当作一个参数传递给具体的状态处理类。

  客户端一般只和环境(Context)交互。客户端可以用状态对象来配置一个环境(Context),一旦配置完毕,就不再需要和状态对象打交道了。客户端通常不负责运行期间状态的维护,也不负责决定后续到底使用哪一个具体的状态处理对象。


策略模式与状态模式的比较

  状态模式主要解决的是当控制一个对象状态转换的条件表达式过于复杂的情况。把状态的判断转移到表示不同状态的一系列类中,可以把复杂判断简化。
  当一个状态的行为取决于他的状态,并且他必须在运行时刻根据状态改变他的行为时,可以考虑使用状态模式。
  比较:从上面这几点,我们可以看出策略模式和状态模式的应用场景有很大的不同:一个是封装一系列平行且复杂多变的实现方式,一个是实现把对象的内在状态的变化封装起来,用外部行为来表现出来。
  总之,虽然二者的类图很相似,但实际上解决的是不同情况的两种场景问题,需要我们去实际分析和判断。



源码下载:http://download.youkuaiyun.com/download/pelifymeng2/9994744


<think>好的,我现在需要回答用户关于Java状态模式的设计与实现的问题,并且根据提供的引用内容来支持回答。首先,我要确保理解状态模式的基本概念和应用场景。 用户提供的引用中,状态模式被定义为一种行为型设计模式,允许对象在内部状态改变时改变其行为,而无需修改其代码。引用[2]提到状态模式通过将行为封装到不同的状态类中,使对象在不同状态下有不同的行为。引用[4]也指出,状态模式在对象内部状态改变时改变其行为,并将行为封装在不同的状态对象中。这些信息帮助我确认状态模式的核心思想是状态与行为的分离。 接下来,用户需要具体的Java示例。引用[3]提到了订单状态的处理,这可能是一个典型的应用场景,比如电商系统中的订单状态机。结合引用[1]中的电子商务项目实例,我可以设计一个订单状态的例子,展示不同状态下的行为变化。 我需要构建一个结构清晰的示例,包括上下文类(Context)和具体的状态类。根据引用中的信息,状态模式的结构通常包含一个抽象状态接口或抽象类,具体状态类实现该接口,上下文类持有当前状态并委托行为给状态对象。 此外,要确保代码符合Java规范,使用合适的设计,比如将状态转换逻辑放在具体状态类中,或者由上下文类管理状态转换。根据引用[3]中的状态机业务实现,可能状态转换由状态类自身触发,比如处理完一个操作后切换到下一个状态。 在编写示例时,需要注意状态之间的转换条件和行为。例如,订单从“待支付”变为“已支付”,再变为“已发货”等。每个状态处理的操作(如支付、发货)会导致状态变更,这需要在具体状态类中实现。 最后,根据用户提供的引用内容,我需要正确标注引用来源。例如,在解释状态模式定义时引用[2]和[4],在提到电商应用时引用[1]和[3]。 现在,我需要将这些思路整合成一个结构清晰的回答,包含定义、结构、实现步骤、示例代码和应用场景,并在适当位置添加引用标注。同时,生成相关问题以帮助用户进一步理解。</think>### Java 状态模式的设计与实现 #### 1. 定义与核心思想 状态模式State Pattern)是一种行为型设计模式,允许对象在内部状态改变时改变其行为。其核心是将**对象的行为封装到不同的状态类**中,通过切换状态对象实现行为的动态调整,避免大量条件判断语句[^2][^4]。 #### 2. 模式结构 - **上下文类(Context)**:持有当前状态对象,定义客户端调用的接口。 - **抽象状态类(State)**:定义状态行为的接口。 - **具体状态类(ConcreteState)**:实现抽象状态类的行为逻辑,可能触发状态转换。 #### 3. 实现步骤(以电商订单状态为例) ##### 步骤1:定义状态接口 ```java public interface OrderState { void pay(OrderContext context); // 支付操作 void ship(OrderContext context); // 发货操作 } ``` ##### 步骤2:实现具体状态类 - **待支付状态** ```java public class UnpaidState implements OrderState { @Override public void pay(OrderContext context) { System.out.println("支付成功"); context.setState(new PaidState()); // 状态切换 } @Override public void ship(OrderContext context) { System.out.println("错误:尚未支付,无法发货"); } } ``` - **已支付状态** ```java public class PaidState implements OrderState { @Override public void pay(OrderContext context) { System.out.println("错误:订单已支付"); } @Override public void ship(OrderContext context) { System.out.println("发货成功"); context.setState(new ShippedState()); // 状态切换 } } ``` ##### 步骤3:定义上下文类 ```java public class OrderContext { private OrderState currentState; public OrderContext() { this.currentState = new UnpaidState(); // 初始状态 } public void setState(OrderState state) { this.currentState = state; } public void pay() { currentState.pay(this); // 委托给当前状态 } public void ship() { currentState.ship(this); } } ``` #### 4. 使用示例 ```java public class Client { public static void main(String[] args) { OrderContext order = new OrderContext(); order.ship(); // 输出:错误:尚未支付,无法发货 order.pay(); // 输出:支付成功 order.ship(); // 输出:发货成功 } } ``` #### 5. 应用场景 - **订单状态流转**:如电商系统中订单的“待支付→已支付→已发货”流程[^3]。 - **游戏角色状态**:角色“站立→移动→攻击”状态切换。 - **硬件设备控制**:打印机“空闲→打印→缺纸”状态管理。 #### 6. 优势与注意事项 - **优势**:消除复杂条件分支、符合开闭原则、状态转换逻辑集中管理[^1][^2]。 - **注意事项**:避免状态类膨胀(需合理拆分粒度)、上下文类与状态类的职责边界需清晰。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值