设计模式(二十一) 状态模式

Posted baiiu

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了设计模式(二十一) 状态模式相关的知识,希望对你有一定的参考价值。

State Pattern

1. 什么是状态模式

状态模式(State Pattern):允许一个对象在其内部状态改变时改变它的行为,对象看起来似乎修改了它的类。
其别名为状态对象(Objects for States),状态模式是一种对象行为型模式。

状态模式用于解决系统中复杂对象的状态转换以及不同状态下行为的封装问题。
当系统中某个对象存在多个状态,这些状态之间可以进行转换,而且对象在不同状态下行为不相同时可以使用状态模式。
状态模式将一个对象的状态从该对象中分离出来,封装到专门的状态类中,使得对象状态可以灵活变化,对于客户端而言,无须关心对象状态的转换以及对象所处的当前状态,无论对于何种状态的对象,客户端都可以一致处理。

状态模式将一个对象在不同状态下的不同行为封装在一个个状态类中,通过设置不同的状态对象可以让环境对象拥有不同的行为,而状态转换的细节对于客户端而言是透明的,方便了客户端的使用。

将与特定状态相关的行为局部化,并且将不同状态的行为分割开来。
将特定状态相关的行为放在一个类中,通过增加新的类可以很容易的实现新的状态和转换。说白了,这样做的目的可以消除庞大的条件分支语句,状态模式通过把各种状态转移逻辑分布到state和子类之间,来减少相互的依赖。

2. 状态模式类角色解析

状态模式结构图:

  • Context(环境类):环境类又称为上下文类,它是拥有多种状态的对象。由于环境类的状态存在多样性且在不同状态下对象的行为有所不同,因此将状态独立出去形成单独的状态类。在环境类中维护一个抽象状态类State的实例,这个实例定义当前状态,在具体实现时,它是一个State子类的对象。
  • State(抽象状态类):它用于定义一个接口以封装与环境类的一个特定状态相关的行为,在抽象状态类中声明了各种不同状态对应的方法,而在其子类中实现类这些方法,由于不同状态下对象的行为可能不同,因此在不同子类中方法的实现可能存在不同,相同的方法可以写在抽象状态类中。
  • ConcreteState(具体状态类):它是抽象状态类的子类,每一个子类实现一个与环境类的一个状态相关的行为,每一个具体状态类对应环境的一个具体状态,不同的具体状态类其行为有所不同。

环境类实际上是真正拥有状态的对象,我们只是将环境类中与状态有关的代码提取出来封装到专门的状态类中。
一个对象的状态之间还可以进行相互转换,通常有两种实现状态转换的方式:

  1. 统一由环境类来负责状态之间的转换
	public void changeState() 
		//判断属性值,根据属性值进行状态转换
      if (value == 0) 
			this.setState(new ConcreteStateA());
		
		else if (value == 1) 
			this.setState(new ConcreteStateB());
		
        // ...
	
  1. 由具体状态类来负责状态之间的转换
   public void changeState(Context ctx) 
		//根据环境对象中的属性值进行状态转换
      if (ctx.getValue() == 1) 
			ctx.setState(new ConcreteStateB());
		
		else if (ctx.getValue() == 2) 
			ctx.setState(new ConcreteStateC());
		
        // ...
	

3. 状态模式优缺点

优点:

  • 封装了状态的转换规则,在状态模式中可以将状态的转换代码封装在环境类或者具体状态类中,可以对状态转换代码进行集中管理,而不是分散在一个个业务方法中。
  • 将所有与某个状态有关的行为放到一个类中,只需要注入一个不同的状态对象即可使环境对象拥有不同的行为。
  • 允许状态转换逻辑与状态对象合成一体,而不是提供一个巨大的条件语句块,状态模式可以让我们避免使用庞大的条件语句来将业务方法和状态转换代码交织在一起。
  • 可以让多个环境对象共享一个状态对象,从而减少系统中对象的个数。

缺点:

  • 状态模式的使用必然会增加系统中类和对象的个数,导致系统运行开销增大。
  • 状态模式的结构与实现都较为复杂,如果使用不当将导致程序结构和代码的混乱,增加系统设计的难度。
  • 状态模式对“开闭原则”的支持并不太好,增加新的状态类需要修改那些负责状态转换的源代码,否则无法转换到新增状态;而且修改某个状态类的行为也需修改对应类的源代码。

适用场景:

  • 对象的行为依赖于它的状态(如某些属性值),状态的改变将导致行为的变化。
  • 在代码中包含大量与对象状态有关的条件语句,这些条件语句的出现,会导致代码的可维护性和灵活性变差,不能方便地增加和删除状态,并且导致客户类与类库之间的耦合增强。

4. 实操感想

判断条件简单,则可以直接写。
如果条件复杂,则考虑将相关的移到状态类实现。但这样可读性岂不是很差?而且要是添加一个状态,所有的状态类都得改?
切换状态是通过new对象,重新set实现,这块在实操中得注意。
状态模式如果扩展为枚举应该清晰一点。

外界不会和状态进行交互,全盘了解状态是 context的工作
每个状态类都持有Context的引用,来实现状态转移
使用状态模式总是会增加设计中类的数目,这是为了要获得程序可扩展性,弹性的代价,如果你的代码不是一次性的,后期可能会不断加入不同的状态,那么状态模式的设计是绝对值得的。这同时也是一个缺点。

状态模式最大的好处是将大量原本根据常量标识的状态的字段替换为类,而原本大量的if…else语句被迁移到类中,这样方便了状态的新增和新加的逻辑。
在复杂的业务场景会需要用到,不要滥用。

5. 代码示例

JAVA 设计模式 : 状态模式

以上是关于设计模式(二十一) 状态模式的主要内容,如果未能解决你的问题,请参考以下文章

设计模式十四—— 状态模式/策略模式

设计模式(二十四)---状态模式

设计模式学习总结(二十三)--状态模式

二十三种设计模式[20] - 状态模式(State Pattern)

设计模式学习笔记(二十二:备忘录模式)

第二十四讲:状态模式