20.状态模式(State Pattern)

1.定义

当一个对象内在状态改变时,允许其改变行为,这个对象看起来像改变了其类。
状态模式的核心是封装。

 

2.要解决的问题-过多的if-else状态判断

对象如何在每一种状态下表现出不同的行为?
在软件开发过程中,应用程序可能会根据不同的情况作出不同的处理。最直接解决方案是将这些所有可能发生的情况全都考虑到。然后使用if... ellse语句来做状态判断来进行不同情况的处理。但是对复杂状态的判断就显得“力不从心了”。随着增加新的状态或者修改一个状体(if else(或switch case)语句的增多或者修改)可能会引起很大的修改,而程序的可读性,扩展性也会变得很弱。维护也会很麻烦。那么我就考虑只修改自身状态的模式。

例子1:按钮来控制一个电梯的状态,一个电梯开们,关门,停,运行。每一种状态改变,都有可能要根据其他状态来更新处理。例如,开门状体,你不能在电梯处于运行状态的时候开门,而是在电梯处于静止状态下才能开门。

例子2:我们给一部手机打电话,就可能出现这几种情况:用户开机,用户关机,用户欠费停机,用户消户等。 所以当我们拨打这个号码的时候:系统就要判断,该用户是否在开机且不忙状态,又或者是关机,欠费等状态。但不管是那种状态我们都应给出对应的处理操作。

 

3.要点

  • 策略模式和状态模式是双胞胎,它们有相同的类图,但是它们的意图不同。策略模式是围绕可以互换的算法来成功创建业务的,然而状态模式是通过改变对象内部的状态来帮助对象控制自己的行为.
  • Context将与状态相关的操作委托给当前的Concrete State对象处理。
  • Context可将自身作为一个参数传递给处理该请求的状态对象。这使得状态对象在必要时可访问Context。
  • Context或Concrete State类都可决定哪个状态是另外哪一个的后继者,以及是在何种条件下进行状态转换。也就是说可以在State中保存对Concrete State的引用,在必要时设置具体的状态,做到状态的转换。
  • 一般来讲,当状态转换是固定的时候,状态转换就适合放在Context中。然而,当转换是更动态的时候,通常会放到具体的状态类中进行。(具体状态类持有Context的引用,实现状态的转换) 。

状态模式类图:

 

4.状态模式的使用场景

  • 行为随状态改变而改变的场景:这也是状态模式的根本出发点,例如权限设计,人员的状态不同即使执行相同的行为结果也会不同,在这种情况下需要考虑使用状态模式。
  • 条件、分支判断语句的替代者:在程序中大量使用switch或者if-else判断语句会导致程序结构不清晰,逻辑混乱,使用状态模式能很好的避免这一问题,它通过扩展子类实现了条件的判断处理。

下面请看状态模式的通用代码:

 

package _20StatePattern;

/**
 * 抽象状态角色
 */
public abstract class State {
	
	// 定义一个环境角色,供子类访问
	protected Context context;

	public Context getContext() {
		return context;
	}

	public void setContext(Context context) {
		this.context = context;
	}
	
	// 行为1
	public abstract void openDoor();
	// 行为2
	public abstract void closeDoor();
}
 
package _20StatePattern;

/**
 * 开门状态
 */
public class OpenDoorState extends State {

	@Override
	public void openDoor() 
	{
		// 当前状态已经是开门了,再执行开门动作就不需要做任何事情
	}

	@Override
	public void closeDoor() {
		// 将状态设置为关门状态
		super.context.setCurrentState(Context.CloseState);
		// 执行关门动作
		super.getContext().closeDoor();
		System.out.println("状态:开门->关门");
	}

}
 
package _20StatePattern;

/**
 * 关门状态
 */
public class CloseDoorState extends State {

	@Override
	public void openDoor() 
	{
		// 将状态设置为开门状态
		super.context.setCurrentState(Context.OpenState);
		// 执行开门动作
		super.getContext().openDoor();
		System.out.println("状态:关门->开门");
	}

	@Override
	public void closeDoor() 
	{
		// 当前状态已经是关门了,再执行关门动作就不需要做任何事情
	}

}
 
package _20StatePattern;

/**
 * 具体环境角色
 */
public class Context{

	// 定义状态
	public final static State OpenState = new OpenDoorState();
	public final static State CloseState = new CloseDoorState();
	
	// 设置当前状态
	private State currentState;

	public State getCurrentState() {
		return currentState;
	}

	public void setCurrentState(State currentState) {
		this.currentState = currentState;
		currentState.setContext(this);
	}

	public void openDoor() {
		this.currentState.openDoor();
	}

	public void closeDoor() {
		this.currentState.closeDoor();
	}
}
 
package _20StatePattern;

/**
 * 场景类
 */
public class Client {

	public static void main(String[] args) {
		Context context = new Context();
//		context.setCurrentState(Context.OpenState);
//		context.closeDoor();
		context.setCurrentState(Context.CloseState);
		context.closeDoor();
	}

}

 5.状态模式的优点

  • 结构清晰:避免了过多的switch……case或者if……else语句,避免了程序的复杂性,提高系统的可维护性
  • 遵循设计原则:很好的体现了开闭原则和单一职责原则,每个状态都是一个子类,你要增加状态就要增加子类,你要修改状态,只要修改一个子类就可以了
  • 封装性非常好:这也是状态模式的基本要求,状态变换放置到类的内部实现,外部的调用不知道内部如何实现和行为的变换

6.状态模式的缺点

状态模式既然有优点,那当然有缺点了。那就是子类会太多,也就是类膨胀,如果一个事物有很多状态也不稀奇,如果完全使用状态模式就会有太多的子类,不好管理,这个需要使用者衡量。

 

7.状态模式的注意事项

状态模式适用于当某个对象在它的状态发生改变时,它的行为也随着发生比较大的变化,也就是说在行为受状态约束的情况下可以使用状态模式,而且使用时对象的状态不要超过5个。

猜你喜欢

转载自wangwengcn.iteye.com/blog/1733230
今日推荐