结构
状态模式(状态对象)允许一个对象在其内部状态改变时改变它的行为。对象看起来似乎修改了它的类。
策略模式包含以下3种角色:
- 抽象状态(State):
是一个接口或者抽象类,用来定义与环境的一个特定状态相关的若干个方法。 - 环境(Context):
含有抽象状态(State)声明的变量,可以引用任何具体的状态类的实例(上转型),用户对该环境类的实例在某种状态下的行为(具体的方法)感兴趣。 - 具体状态(ConcreteState):
实现抽象状态的类(或是继承抽象类)。
状态模式中的角色UML类图如下图:
优点
- 使用一个类封装对象的一种状态,很容易增加新的状态。
- 环境中不必出现大量的条件判断语句,环境实例所呈现的状态变得更加清晰,容易理解。
- 可以让用户程序很方便地切换环境实例的状态。
- 不会让环境的实例中出现内部状态不一致的情况。
适用场景
- 一个对象的行为依赖于它的状态,并且它必须在运行时刻根据状态改变它的相关行为。
- 需要编写大量的条件分支语句来决定一个操作的行为,而且这些条件恰好表示了对象的其中一种状态。
状态切换
环境实例在某种状态下执行一个方法后,可能导致该实例的状态发生变化。通过使用状态模式可以方便地将环境实例从一个状态切换为宁一个状态,从而避免了大量的条件判断语句的嵌套。
当一个环境实例有确定的若干个状态时,可以由环境实例本身负责状态的切换。
该环境实例中可以含有所有状态的引用,并提供设置改变状态的方法,如setState(State state) 方法供状态进行自主切换。
注:在状态类中依赖环境类,在状态类中通过环境类的setState(State state)方法,切换环境实例中的状态。
实例——咖啡自动售货机
本问题中,需要设计一个咖啡自动售货机,当用户投入硬币后,获得一杯咖啡。
咖啡自动售货机一共有三种状态,分别是 “有咖啡,无人投币” 、“有咖啡,有人投币” 和 “无咖啡”。咖啡自动售货机有两个方法:needAnCoin() 和 sellCoffee()。
抽象状态(State):
本问题中,根据需求,抽象出State抽象状态类。
抽象类中有一个变量coffeeCount,用来记录咖啡的数量。
抽象类中有两个方法,分别是 giveAnCupCoffee() 和 comeInCoin(),前者在用户选择购买咖啡时调用,后者在用户投入硬币时被调用。
State.java:
public abstract class State {
/**
* 记录咖啡数量
*/
int coffeeCount;
public abstract void giveAnCoffee();
public abstract void comeInCoin();
}
具体状态(ConcreteState):
对于本问题,一共有三个具体状态角色分别是HaveCoffeeNoCoin(有咖啡无人投币) 、 HaveCoffeeAndCoin(有咖啡有人投币) 和 HaveNotCoffee(无咖啡) ,分开进行讨论。
(1)HaveCoffeeNoCoin(有咖啡无人投币)
咖啡自动售货机的默认初始状态为HaveCoffeeNoCoin(有咖啡无人投币),当处于此状态时,调用sellCoffee() 方法将显示“请投币”,并保持此状态;
...
@Override
public void giveAnCoffee() {
System.out.println("请投币");
}
...
调用needAnCoin() 方法将显示“已投入硬币,请稍等”,然后将状态切换为HaveCoffeeAndCoin(有咖啡有人投币) 状态。
...
@Override
public void comeInCoin() {
System.out.println("已投入硬币,请稍等");
autoCoffeeMachine.setState(autoCoffeeMachine.haveCoffeeAndCoin);
}
...
HaveCoffeeAndCoin.java:
public class HaveCoffeeNoCoin extends State {
AutoCoffeeMachine autoCoffeeMachine;
HaveCoffeeNoCoin(AutoCoffeeMachine autoCoffeeMachine){
this.autoCoffeeMachine = autoCoffeeMachine;
}
@Override
public void giveAnCoffee() {
System.out.println("请投币");
}
@Override
public void comeInCoin() {
System.out.println("已投入硬币,请稍等");
//切换到有咖啡有人投币状态
autoCoffeeMachine.setState(autoCoffeeMachine.haveCoffeeAndCoin);
}
}
(2)HaveCoffeeAndCoin(有咖啡有人投币)
当处于此状态时,调用sellCoffee() 方法将显示“咖啡已被送出”,然后根据剩余咖啡数量,决定切换状态为HaveCoffeeAndCoin(有咖啡有人投币) 或是 HaveNotCoffee(无咖啡) ;
...
@Override
public void giveAnCoffee() {
int n = autoCoffeeMachine.haveCoffeeNoCoin.coffeeCount;
if(n>1){
n--;
System.out.println("咖啡已被送出");
autoCoffeeMachine.haveCoffeeNoCoin.coffeeCount=n;
autoCoffeeMachine.setState(autoCoffeeMachine.haveCoffeeNoCoin);
}else if(n ==1){
n--;
System.out.println("咖啡已被送出");
autoCoffeeMachine.haveCoffeeNoCoin.coffeeCount=n;
autoCoffeeMachine.setState(autoCoffeeMachine.haveNotCoffee);
}
}
...
HaveCoffeeAndCoin.java:
public class HaveCoffeeAndCoin extends State {
AutoCoffeeMachine autoCoffeeMachine;
HaveCoffeeAndCoin(AutoCoffeeMachine autoCoffeeMachine){
this.autoCoffeeMachine = autoCoffeeMachine;
}
@Override
public void giveAnCoffee() {
int n = autoCoffeeMachine.haveCoffeeNoCoin.coffeeCount;
if(n>1){
n--;
System.out.println("咖啡已被送出");
autoCoffeeMachine.haveCoffeeNoCoin.coffeeCount=n;
//切换到有咖啡无人投币状态
autoCoffeeMachine.setState(autoCoffeeMachine.haveCoffeeNoCoin);
}else if(n ==1){
n--;
System.out.println("咖啡已被送出");
autoCoffeeMachine.haveCoffeeNoCoin.coffeeCount=n;
//切换到无咖啡状态
autoCoffeeMachine.setState(autoCoffeeMachine.haveNotCoffee);
}
}
@Override
public void comeInCoin() {
System.out.println("目前不允许投币");
}
}
(3)HaveNotCoffee(无咖啡)
当处于此状态时,调用sellCoffee() 方法将显示“没有咖啡了,请联系10086进行补给”,调用 comeInCoin() 方法将显示“投币无效,退回”。
HaveNotCoffee.java:
public class HaveNotCoffee extends State {
AutoCoffeeMachine autoCoffeeMachine;
HaveNotCoffee(AutoCoffeeMachine autoCoffeeMachine){
this.autoCoffeeMachine = autoCoffeeMachine;
}
@Override
public void giveAnCoffee() {
System.out.println("没有咖啡了,请联系10086进行补给");
}
@Override
public void comeInCoin() {
System.out.println("投币无效,退回");
}
}
环境(Context):
环境类命名为AutoCoffeeMachine。代码如下:
AutoCoffeeMachine.java:
public class AutoCoffeeMachine {
State haveCoffeeNoCoin, haveCoffeeAndCoin, haveNotCoffee;
State state;
AutoCoffeeMachine(){
init();
}
public void init(){
haveCoffeeNoCoin = new HaveCoffeeNoCoin(this);
haveCoffeeAndCoin = new HaveCoffeeAndCoin(this);
haveNotCoffee = new HaveNotCoffee(this);
//咖啡机的默认状态是有咖啡无人投币
this.setState(haveCoffeeNoCoin);
//咖啡机初始有3杯咖啡
this.state.coffeeCount = 3;
}
public void sellCoffee(){
state.giveAnCoffee();
}
public void needAnCoin(){
state.comeInCoin();
}
public void setState(State state) {
this.state = state;
}
}
Application.java:
public class Application {
public static void main(String[] args){
AutoCoffeeMachine autoCoffeeMachine = new AutoCoffeeMachine();
autoCoffeeMachine.sellCoffee();
autoCoffeeMachine.needAnCoin();
autoCoffeeMachine.sellCoffee();
autoCoffeeMachine.needAnCoin();
autoCoffeeMachine.sellCoffee();
autoCoffeeMachine.needAnCoin();
autoCoffeeMachine.sellCoffee();
autoCoffeeMachine.needAnCoin();
autoCoffeeMachine.sellCoffee();
}
}
程序运行效果图如下: