产生背景:类的实例在应对需求变化时(状态变化时)缺乏弹性,通过某些条件判断语句判断对象的需求(此处指状态)时,不能很好的满足用户的需求。因此,按照面向对象、不面向实现的设计原则,应当将对象的状态从当前对象中分离出去,或者说将一个对象的状态封装在另外一个类中。
1.GOF引用:允许一个对象在其内部状态改变时改变它的行为,对象看起来似乎修改了它的类。
2.关键点:将对象的状态封装成为独立的类(不同状态封装在不同的具体状态类中,有几个状态就定义几个状态类),对象调用方法时,可以委托当前对象所具有的状态(状态对象)调用相应的方法,使当前对象看起来好像修改了它的类。
3.模式的三种角色:
环境(Context):该类含有抽象状态声明的变量,可以引用具体状态类的实例。用户对环境类的实例在某种状态下的行为感兴趣。(即间接调用状态对象的处理方法)
抽象状态(State):一个接口或抽象类。定义了与环境的一个特定状态相关的若干个方法
具体状态(Concrete State):实现(扩展)抽象状态(抽象类)的类。
4.类图:
5. 状态模式举例(代码示例)
问题描述如下:设计带文字信息的温度计,根据不同的温度(状态)提示不同的温度信息。
类图:
具体代码如下:
package com.StatePattern;
public abstract class TemperatureState {
public abstract void showTemperature() ;
}
package com.StatePattern;
public class LowState extends TemperatureState {
double n=0;
public LowState(double n) {
super();
if(n<=0){
this.n = n;
}
}
public void showTemperature() {
System.out.println("现在温度是"+n+"属于低温度");
}
}
package com.StatePattern;
public class MiddleState extends TemperatureState {
double n=15;
public MiddleState(double n) {
super();
if(n>0&&n<26){
this.n = n;
}
}
public void showTemperature() {
System.out.println("现在温度是"+n+"属于正常温度");
}
}
package com.StatePattern;
public class HeightState extends TemperatureState {
double n=39;
public HeightState(double n) {
super();
if(n>=39){
this.n = n;
}
}
public void showTemperature() {
System.out.println("现在温度是"+n+"属于高温度");
}
}
package com.StatePattern;
public class Application {
public static void main(String[] args) {
TemperatureState state=new LowState(-12);
Thermometer thermometer=new Thermometer();
thermometer.setState(state);
thermometer.showMessage();
state=new MiddleState(20);
thermometer.setState(state);
thermometer.showMessage();
state=new HeightState(39);
thermometer.setState(state);
thermometer.showMessage();
}
}
6.状态切换:
环境实例在某种状态下执行一个方法后可能导致该实例的状态发生改变。使用状态模式很容易实现状态切换。当环境有若干个状态时,可以由环境实例本身负责状态的切换,即提供设置改变状态的方法,(但是在具体状态类中进行处理切换)比如setState()方法。并且环境实例可以含有所有状态的引用。
举例:使用他弹夹大小为3颗子弹的手枪通过更换弹夹重新获取子弹。
状态图:
类图:
代码示例:
package com.StateChange;
public class Gun {
private State stateOne;
private State stateTwo;
private State stateThree;
private State stateNull;
private State state;
public void fire() {
state.fire();
}
public void loadBullet() {
state.loadBullet();
}
public Gun() {
stateThree = new BulletStateThree(this);
stateTwo = new BulletStateTwo(this);
stateOne = new BulletStateOne(this);
stateNull = new BulletStateNull(this);
state = stateThree; // 手枪的默认状态是有3颗子弹
}
public State getStateOne() {
return stateOne;
}
public State getStateTwo() {
return stateTwo;
}
public State getStateThree() {
return stateThree;
}
public State getStateNull() {
return stateNull;
}
public void setState(State state) {
this.state = state;
}
}
package com.StateChange;
public abstract class State {
public abstract void fire();
public abstract void loadBullet();
public abstract String showStateMess();
}
package com.StateChange;
public class BulletStateThree extends State {
Gun gun;
public BulletStateThree(Gun gun) {
this.gun = gun;
}
public void fire() {
System.out.print("射出1颗子弹");
gun.setState(gun.getStateTwo());
System.out.println("(进入"+gun.getStateTwo().showStateMess()+")");
}
public void loadBullet() {
System.out.println("无法装弹");
}
public String showStateMess() {
return "3颗子弹状态";
}
}
package com.StateChange;
public class BulletStateTwo extends State {
Gun gun;
public BulletStateTwo(Gun gun) {
this.gun = gun;
}
public void fire() {
System.out.print("射出1颗子弹");
gun.setState(gun.getStateOne());
System.out.println("(进入"+gun.getStateOne().showStateMess()+")");
}
public void loadBullet() {
System.out.println("无法装弹");
}
public String showStateMess() {
return "2颗子弹状态";
}
}
package com.StateChange;
public class BulletStateOne extends State {
Gun gun;
public BulletStateOne(Gun gun) {
super();
this.gun = gun;
}
public void fire() {
System.out.print("射出最后1颗子弹");
gun.setState(gun.getStateNull());
System.out.println("(进入"+gun.getStateNull().showStateMess()+")");
}
public void loadBullet() {
System.out.println("无法装弹");
}
public String showStateMess() {
return "1颗子弹状态";
}
}
package com.StateChange;
public class BulletStateNull extends State {
Gun gun;
public BulletStateNull(Gun gun) {
super();
this.gun = gun;
}
public void fire() {
System.out.print("不能射出子弹");
System.out.println("(目前是"+showStateMess()+")");
}
public void loadBullet() {
System.out.print("装弹");
gun.setState(gun.getStateThree());
System.out.println("(进入"+gun.getStateThree().showStateMess()+")");
}
public String showStateMess() {
return "无子弹状态";
}
}
package com.StateChange;
public class Application {
public static void main(String[] args) {
Gun gun=new Gun();
gun.fire();
gun.fire();
gun.fire();
gun.fire();
gun.loadBullet();
gun.fire();
}
}
7.状态模式和策略模式的比较
状态进行建模时,状态迁移是一个核心内容;
策略模式时,迁移与此毫无关系。另外,策略模式(此处待补全,敬请谅解)
8.状态模式的优点:
① 使用一个类封装对象的一种状态,很容易增加新的状态。
② 环境Context中不必出现大量的条件判断语句。而是封装在具体的状态类中。环境所呈现的状态变得更加清晰、容易理解。
③ 使用状态模式可以让用户程序委托内部具体状态类切换环境实例的状态。
④ 不会让环境的实例中出现内部状态不一致的情况
⑤ 状态对象没有实例变量,环境的各个实例可以共享一个状态对象。
9.适合使用状态模式的情景:
① 一个对象的行为依赖于它的状态,并且它必须在运行时根据状态改变它的行为。
② 需要大量的条件分支语句来决定一个操作的行为,而且这些条件恰好表示对象的一种状态。