前言
大家好,我是练习两年半的Java练习生,最近阅读了《深入浅出设计模式(中文版)》,学习了各种设计模式,所以想出一个专栏和大家分享一下!
如果大家觉得文章还可以,欢迎关注点赞!后续还会陆续更新!!
一、定义
状态模式允许对象在内部状态改变时改变它的行为,对象看起来好像修改了它的类。
其中,对象看起来好像修改了它的类 是什么意思呢?从客户的视角来说,如果你使用的对象能够完全改变它的行为,那么你会觉得这个类变了,实际上,这个类只是使用组合通过简单引用不同对象来造成类改变的假象。
二、类图
问: 这个类图与什么模式的类图相似?
这个类图与策略模式的类图一样,但两者区别在于“意图”。
对状态模式来说,我们将一群行为封装在状态对象中,context的行为随时可以委托到状态对象中的一个。随着时间的流逝,当前状态在状态对象集合中游走改变,以反映除context内部的状态。但客户对状态对象了解不多。
而以策略模式而言,客户通常主动指定Context)所要组合的策略对象是哪一个。现在,固然策略模式让我们具有弹性,能够在运行时改变策略,但对于某个context对象来说,通常都只有一个最适当的策略对象。比方说,在前面,有些鸭子(例如绿头鸭)被设置成利用典型的飞翔行为进行飞翔,而有些鸭子(例如橡皮鸭和诱饵鸭)使用的飞翔行为只能让他们紧贴地面。
三、应用
3.1 需求
假设有一个咖啡机,它可以制作两种咖啡:拿铁和美式咖啡。咖啡机的状态分为“空闲状态”、“工作状态”和“维修状态”三种。当咖啡机处于“空闲状态”时,它可以接收用户的订单,并开始制作咖啡。当咖啡机处于“工作状态”时,它正在制作咖啡,此时不能接受新的订单。当咖啡机处于“维修状态”时,它不能接收订单,因为需要进行维护。
3.2 分析
3.3 实现
下面是使用状态模式实现上述场景的示例代码:
// 抽象状态类
public abstract class State {
protected CoffeeMachine coffeeMachine;
public State(CoffeeMachine coffeeMachine) {
this.coffeeMachine = coffeeMachine;
}
// 投入硬币
public abstract void coin();
// 退回硬币
public abstract void backCoin();
// 转动手柄
public abstract void turnHandle();
// 出售咖啡
public abstract void sellCoffee();
}
// 具体状态类:待支付状态
public class NoCoinState extends State {
public NoCoinState(CoffeeMachine coffeeMachine) {
super(coffeeMachine);
}
@Override
public void coin() {
System.out.println("投入了一枚硬币");
coffeeMachine.setState(coffeeMachine.getCoinInsertedState());
}
@Override
public void backCoin() {
System.out.println("您没有投入硬币,无法退币");
}
@Override
public void turnHandle() {
System.out.println("请先投入硬币");
}
@Override
public void sellCoffee() {
System.out.println("请先投入硬币");
}
}
// 具体状态类:已支付状态
public class CoinInsertedState extends State {
public CoinInsertedState(CoffeeMachine coffeeMachine) {
super(coffeeMachine);
}
@Override
public void coin() {
System.out.println("您已经投过币了,无需再投");
}
@Override
public void backCoin() {
System.out.println("退回了一枚硬币");
coffeeMachine.setState(coffeeMachine.getNoCoinState());
}
@Override
public void turnHandle() {
System.out.println("转动手柄,出咖啡中...");
coffeeMachine.setState(coffeeMachine.getSoldState());
}
@Override
public void sellCoffee() {
System.out.println("请先转动手柄");
}
}
// 具体状态类:售出状态
public class SoldState extends State {
public SoldState(CoffeeMachine coffeeMachine) {
super(coffeeMachine);
}
@Override
public void coin() {
System.out.println("请等待咖啡出完再投币");
}
@Override
public void backCoin() {
System.out.println("咖啡已经在制作,无法退币");
}
@Override
public void turnHandle() {
System.out.println("转动手柄,正在出咖啡,请耐心等待...");
}
@Override
public void sellCoffee() {
System.out.println("正在出咖啡,请耐心等待...");
coffeeMachine.setState(coffeeMachine.getNoCoinState());
}
}
// 咖啡机类,使用状态模式实现
public class CoffeeMachine {
private State noCoinState;
private State coinInsertedState;
private State soldState;
private State currentState;
public CoffeeMachine() {
noCoinState = new NoCoinState(this);
coinInsertedState = new CoinInsertedState(this);
soldState = new SoldState(this);
// 初始状态为未投币状态
currentState = noCoinState;
// 初始化咖啡数量
coffeeCount = 10;
}
// 投币方法
public void insertCoin() {
currentState.insertCoin();
}
// 退币方法
public void ejectCoin() {
currentState.ejectCoin();
}
// 购买咖啡方法
public void buyCoffee() {
currentState.buyCoffee();
}
// 获取当前咖啡数量
public int getCoffeeCount() {
return coffeeCount;
}
// 减少咖啡数量
public void decreaseCoffeeCount() {
coffeeCount--;
}
// 切换状态为未投币状态
public void setCurrentStateToNoCoinState() {
currentState = noCoinState;
}
// 切换状态为已投币状态
public void setCurrentStateToCoinInsertedState() {
currentState = coinInsertedState;
}
// 切换状态为售出状态
public void setCurrentStateToSoldState() {
currentState = soldState;
}
}
四、问题
客户会直接与状态交互吗?
答:不会。状态是用在Context中来代表它的内部状态以及行为的,所以只有Context才会对状态提出求。客户不会直接改变Context的状态。全盘了解状态是Context的工作,客户根本不了解,所以不会直接和状态联系。
如果在我的程序中Context有许多实例,这些实例之间可以共享状态对象吗?
是的,绝对可以,事实上这是很常见的做法。但唯一的前提是,你的状态对象不能持有它们自己的内部状态;否则就不能共享。想要共享状态,你需要把每个状态都指定到静态的实例变量中。如果你的状态需要利用到Context中的方法或者实例变量,你还必须在每个handler()方法内传入一个context的引用。
五、总结
以上就是今天要讲的内容,本文介绍了设计模式中的状态模式,要注意其与策略模式的区别,两者的目的并不相同。