这是我参与8月更文挑战的第8天,活动详情查看:8月更文挑战java
欢迎来到今天的学习,今天咱们一块儿来学习下你可能不多据说,可是随处可用的----状态模式。多唠叨几句,我本月将会对java的设计模式精讲,欢迎点击头像,关注个人专栏,我会持续更新,加油!spring
系列文章:设计模式
设计模式之单例模式markdown
设计模式之代理模式ide
设计模式之访问者模式post
设计模式之适配器模式单元测试
...持续更新中
话很少说,进入正题
顾名思义,应用场景确定是要随时改变某个事件业务的状态,好比咱们的支付的订单订单,咱们购买商品到用户收货完成的一系列,均可以用到状态模式。
它的原始定义是:容许一个对象在其内部状态改变时改变它的行为,对象看起来彷佛修改了本身的类同样。
简单来讲,状态模式就是让一个对象经过定义一系列状态的变化来控制行为的变化,好比咱们提到的线上商品购买。给购买的物品定义几个包裹运送状态,已下单、运送中、已签收等状态的调整,也就是说,当包裹的状态发生改变时,就会触发相应的外部操做。
咱们看下图。大概就是这样子(此图来源于网络):
从图中,咱们能看出状态模式包含的关键角色有三个:
上下文信息类(Context):实际上就是存储当前状态的类,对外提供更新状态的操做。(通常你看到Context字眼 上下文信息类,spring当中有不少相似的)
抽象状态类(State):能够是一个接口或抽象类,用于定义声明状态更新的操做方法有哪些
具体状态类(StateA 等):实现抽象状态类定义的方法,根据具体的场景来指定对应状态改变后的代码实现逻辑
接下来咱们拿支付订单状态实际场景结合代码来深刻理解下状态模式
为了帮助你更好地理解状态模式的适用场景,下面咱们仍是经过一个简单的例子来演示一下。在用户支付一笔订单的过程当中,当咱们选定好了商品提交订单后,用户点击支付,拉起支付,到最后的支付成功。这里咱们定义 4 种简单的订单状态:建立订单、支付中、取消支付、支付成功。以下图所示:
咱们针对上面的简图来用状态模式代码实现下业务
首先,咱们来定义订单的状态 OrderState,在接口中声明一个更新状态的方法 updateState(),该方法接收订单上下文信息类 OrderContext 做为参数。
//定义订单状态接口
public interface OrderState {
/** * 定义了4种状态 * 1 - 建立订单 * 2 - 订单支付中 * 3 - 用户中途取消支付 * 4 - 支付成功 * @param ctx */
void updateState(OrderContext ctx);
}
复制代码
而后咱们再来详细定义上下文信息类 orderContext,其中包含一个当前状态 OrderState 和一个订单编号(orderNo)
public class OrderContext {
private OrderState currentState; //订单状态
private String orderNo; //订单编号
public OrderContext(OrderState currentState, String orderNo) {
this.currentState = currentState;
this.orderNo = orderNo;
//默认初始化建立订单
if(currentState == null) {
this.currentState = Acknowledged.instance();
}
}
public OrderState getCurrentState() {
return currentState;
}
public void setCurrentState(OrderState currentState) {
this.currentState = currentState;
}
public String getOrderNo() {
return orderNo;
}
public void setOrderNo(String orderNo) {
this.orderNo = orderNo;
}
//开始修改状态(当前对象携带的信息)
public void update() {
currentState.updateState(this);
}
}
复制代码
接下来,咱们依次定义具体的状态类:建立订单(CreateOrder)、订单支付(OrderProcessing)、取消支付(CancelOrder)、支付成功(OrderPaySuccess)。每个类都会实现 updateState() 方法,同时使用单例模式模拟状态的惟一性。
//建立订单
public class CreateOrder implements OrderState {
//Singleton
private static CreateOrder instance = new CreateOrder();
private CreateOrder() {}
public static CreateOrder instance() {
return instance;
}
@Override
public void updateState(OrderContext ctx) {
System.out.println("->. -> 开始建立订单");
//建立完订单 将订单修改成订单支付中......
ctx.setCurrentState(OrderProcessing.instance());
}
}
//订单支付中
public class OrderProcessing implements OrderState {
//Singleton
private static OrderProcessing instance = new OrderProcessing();
private OrderProcessing() {}
public static OrderProcessing instance() {
return instance;
}
@Override
public void updateState(OrderContext ctx) {
System.out.println("-> -> 订单支付中");
//接着改成支付成功(不考虑用户中途取消)
ctx.setCurrentState(InTransition.instance());
}
}
//订单支付成功
public class OrderPaySuccess implements OrderState {
//Singleton
private static OrderPaySuccess instance = new OrderPaySuccess();
private OrderPaySuccess() {}
public static OrderPaySuccess instance() {
return instance;
}
@Override
public void updateState(OrderContext ctx) {
System.out.println("-> -> 用户支付成功 end");
}
}
复制代码
最后,咱们运行一个单元测试,经过执行上下文信息类的更新操做了变动状态。
public class Client {
public static void main(String[] args) {
OrderContext ctx = new OrderContext(null, "123456789");
ctx.update();
ctx.update();
}
}
//输出
->. -> 开始建立订单
-> -> 订单支付中
-> -> 用户支付成功 end
复制代码
OK,到这里咱们的代码部分就结束了。可能会有人问了,为何使用状态模式呢,缘由主要有如下两个:
第一个,下降耦合性。当要设计的业务具备复杂的状态变迁时,咱们指望经过状态变化来快速进行变动操做,并下降代码耦合性。
第二个避免增长代码的复杂性,要否则就得写满屏的if eles
写到这里突然想到 还有个例子更加让你容易理解,就是线程池的五种状态:一个线程的生命周期里有五种状态,只有在得到当前状态后才能肯定下一个状态。
有得必有舍,任何模式都是这样的,咱们考虑下状态模式的缺点。这样之后有利于咱们更好的优化代码:
形成不少零散类。 状态模式由于须要对每个状态定义一个具体状态类,因此势必会增长系统类和对象的个数
状态切换关系越复杂,代码实现难度越高,若是业务及其复杂,又比较耗时,不建议此中模式,应采用异步形式。
不知足开闭原,若是要改逻辑,好比要动具体里面的业务代码。
OK,状态模式 今天咱们就讲到这里了!
感谢你的阅读,若是你感受学到了东西,麻烦您点赞,关注。
我已经将本章收录在专题里,点击下方专题,关注专栏,我会天天发表干货,本月我会持续输入设计模式。
加油! 咱们下期再见!