文章目录
前言
本专栏主要讲述设计模式在实际开发场景中的运用, 如果对设计模式有一定的基础可以更好的理解, 当然, 我会把整个demo完整的贴出来, 尽量注释清楚, 让没有设计模式基础的也可以稍微改改运用在自己的项目中, 这里对设计模式的具体角色构成不多赘余。
状态模式在处理多状态且复杂的业务, 可以把状态变化与业务解耦, 对繁杂的业务梳理处理更简单清晰不容易出错, 多大数项目中都可以运用, 所以本专栏把状态模式放在第一篇
一、状态模式使用场景及优缺点
定义:对有状态的对象,把复杂的判断逻辑提取到不同的状态对象中,允许状态对象在其内部状态发生改变时改变其行为
使用场景:
1. 当一个对象的行为取决于它的状态,并且它必须在运行时根据状态改变它的行为时,就可以考虑使用状态模式
2. 一个操作中含有庞大的分支结构,并且这些分支决定于对象的状态时
优点:
1. 将所有与某个状态有关的行为放到一个类中,并且可以方便地增加新的状态,只需要改变对象状态即可改变对象的行为
2. 允许状态转换逻辑与状态对象合成一体,而不是某一个巨大的条件语句块
缺点:
1. 状态模式的使用必然会增加系统类和对象的个数
2. 状态模式的结构与实现都较为复杂,如果使用不当将导致程序结构和代码的混乱
3. 状态模式对"开闭原则"的支持并不太好
二、实际开发场景中的运用
1.实体类
这里以订单状态为例: 订单状态有 1待付款 2待派送 3派送中 4已完成 5已取消
具体状态转变代码有注释
package com.practical.behaviorType.state.domain;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.Date;
/**
* 订单表
*
* @TableName orders
*/
@TableName(value = "orders")
@Data
public class Orders implements Serializable {
/**
* 主键
*/
@TableId
private Long id;
private String number;
private Long userId;
/**
* 订单状态
* 1待付款: 可以从 1待付款 -> 5已取消/2待派送
* 2待派送: 可以从 2待派送 -> 5已取消/3派送中
* 3派送中: 只能从 3派送中 -> 4已完成
* 4已完成: 不可操作
* 5已取消: 不可操作
*/
private Integer status;
private Long addressBookId;
private Date orderTime;
private Date checkoutTime;
private Integer payMethod;
private BigDecimal amount;
private String remark;
private String phone;
private String address;
private String userName;
private String consignee;
@TableField(exist = false)
private static final long serialVersionUID = 1L;
}
2.定义状态类
1. 抽象状态类
package com.practical.behaviorType.state.orderStatus;
import com.practical.behaviorType.state.service.OrdersStatusService;
/**
* 订单状态 抽象父类
* <p>
* 1待付款: 可以从 1待付款 -> 5已取消/2待派送
* 2待派送: 可以从 2待派送 -> 5已取消/3派送中
* 3派送中: 只能从 3派送中 -> 4已完成
* 4已完成: 不可操作
* 5已取消: 不可操作
*/
public abstract class OrdersStatus {
//修改订单service(状态模式环境类)
private OrdersStatusService ordersStatusService;
//订单状态码
protected Integer code;
public void setOrdersStatusService(OrdersStatusService ordersStatusService) {
this.ordersStatusService = ordersStatusService;
}
//强制子类指定各自状态码
public OrdersStatus(Integer code) {
this.code = code;
}
//付款操作 1待付款 -> 2待派送
public abstract void pay(Long orderId);
//派送操作 2待派送 -> 3派送中
public abstract void send(Long orderId);
//送达操作 3派送中 -> 4已完成
public abstract void delivery(Long orderId);
//取消操作 1待付款/2待派送 -> 5已取消
public abstract void canceled(Long orderId);
public Integer getCode() {
return code;
}
}
2.具体状态类
状态模式最重要的部分: 在各种状态内部既各状态具体实现类中 编写 各自业务 从 当前状态 执行改变状态方法时 的校验, 修改状态等操作, 下面只是一个简单的例子, 大家自行根据业务编写校验逻辑
package com.practical.behaviorType.state.orderStatus;
import com.practical.behaviorType.state.constant.OrdersStatusConstant;
import com.practical.behaviorType.state.domain.Orders;
import com.practical.behaviorType.state.service.OrdersService;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
/**
* 待支付状态: 1
*
* @author zlj
* @since 2023/8/10
*/
@Component
public class ObligationStatus extends OrdersStatus {
@Resource
private OrdersService ordersService;
//指定状态码
public ObligationStatus() {
super(1);
}
@Override
public void pay(Long orderId) {
//修改数据库状态为待派送
ordersService.lambdaUpdate()
.set(Orders::getStatus, OrdersStatusConstant.WAITING_STATUS.code)
.eq(Orders::getId, orderId)
.update();
//模拟
System.out.println("订单: " + orderId + ", 开始派送");
}
@Override
public void send(Long orderId) {
throw new RuntimeException("此订单还未支付, 不可派送");
}
@Override
public void delivery(Long orderId) {
throw new RuntimeException("此订单还未支付, 不可送达");
}
@Override
public void canceled(Long orderId) {
//修改数据库状态为取消订单
ordersService.lambdaUpdate()
.set(Orders::getStatus, OrdersStatusConstant.CANCELED_STATUS.code)
.eq(Orders::getId, orderId)
.update();
//模拟
System.out.println("订单: " + orderId + ", 已取消");
}
}
package com.practical.behaviorType.state.orderStatus;
import com.practical.behaviorType.state.constant.OrdersStatusConstant;
import com.practical.behaviorType.state.domain.Orders;
import com.practical.behaviorType.state.service.OrdersService;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
/**
* 待派送状态 2
*
* @author zlj
* @since 2023/8/10
*/
@Component
public class WaitingStatus extends OrdersStatus {
@Resource
private OrdersService ordersService;
public WaitingStatus() {
super(2);
}
@Override
public void pay(Long orderId) {
throw new RuntimeException("此订单已支付, 不可重复支付");
}
@Override
public void send(Long orderId) {
//修改数据库状态为3派送中
ordersService.lambdaUpdate()
.set(Orders::getStatus, OrdersStatusConstant.SENDING_STATUS.code)
.eq(Orders::getId, orderId)
.update();
//模拟
System.out.println("订单: " + orderId + ", 开始派送");
}
@Override
public void delivery(Long orderId) {
throw new RuntimeException("此订单还未派送, 不能完成订单");
}
@Override
public void canceled(Long orderId) {
//修改数据库状态为取消订单
ordersService.lambdaUpdate()
.set(Orders::getStatus, OrdersStatusConstant.CANCELED_STATUS.code)
.eq(Orders::getId, orderId)
.update();
//模拟
System.out.println("订单: " + orderId + ", 已取消");
}
}
package com.practical.behaviorType.state.orderStatus;
import com.practical.behaviorType.state.constant.OrdersStatusConstant;
import com.practical.behaviorType.state.domain.Orders;
import com.practical.behaviorType.state.service.OrdersService;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
/**
* 派送中状态 3
*
* @author zlj
* @since 2023/8/10
*/
@Component
public class SendingStatus extends OrdersStatus {
@Resource
private OrdersService ordersService;
public SendingStatus() {
super(3);
}
@Override
public void pay(Long orderId) {
throw new RuntimeException("此订单已支付, 不可重复支付");
}
@Override
public void send(Long orderId) {
throw new RuntimeException("此订单正在派送");
}
@Override
public void delivery(Long orderId) {
//修改数据库状态为已完成
ordersService.lambdaUpdate()
.set(Orders::getStatus, OrdersStatusConstant.FINISH_STATUS.code)
.eq(Orders::getId, orderId)
.update();
//模拟
System.out.println("订单: " + orderId + ", 已完成");
}
@Override
public void canceled(Long orderId) {
throw new RuntimeException("此订单正在派送, 不可取消订单");
}
}
package com.practical.behaviorType.state.orderStatus;
import com.practical.behaviorType.state.service.OrdersService;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
/**
* 已完成状态 4
*
* @author zlj
* @since 2023/8/10
*/
@Component
public class FinishStatus extends OrdersStatus {
@Resource
private OrdersService ordersService;
public FinishStatus() {
super(4);
}
@Override
public void pay(Long orderId) {
throw new RuntimeException("此订单已完成, 不可操作");
}
@Override
public void send(Long orderId) {
throw new RuntimeException("此订单已完成, 不可操作");
}
@Override
public void delivery(Long orderId) {
throw new RuntimeException("此订单已完成, 不可操作");
}
@Override
public void canceled(Long orderId) {
throw new RuntimeException("此订单已完成, 不可操作");
}
}
package com.practical.behaviorType.state.orderStatus;
import com.practical.behaviorType.state.service.OrdersService;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
/**
* 已取消状态 5
*
* @author zlj
* @since 2023/8/10
*/
@Component
public class CanceledStatus extends OrdersStatus {
@Resource
private OrdersService ordersService;
public CanceledStatus() {
super(5);
}
@Override
public void pay(Long orderId) {
throw new RuntimeException("此订单已取消, 不可操作");
}
@Override
public void send(Long orderId) {
throw new RuntimeException("此订单已取消, 不可操作");
}
@Override
public void delivery(Long orderId) {
throw new RuntimeException("此订单已取消, 不可操作");
}
@Override
public void canceled(Long orderId) {
throw new RuntimeException("此订单已取消, 不可操作");
}
}
3.定义状态常量
这里用了hutool的工具类SpringUtil.getBean()从springboot中获取bean
package com.practical.behaviorType.state.constant;
import cn.hutool.extra.spring.SpringUtil;
import com.practical.behaviorType.state.orderStatus.*;
/**
* 状态常量
*
* @author zlj
* @since 2023/8/10
*/
public interface OrdersStatusConstant {
//1待付款
ObligationStatus OBLIGATION_STATUS = SpringUtil.getBean(ObligationStatus.class);
//2待派送
WaitingStatus WAITING_STATUS = SpringUtil.getBean(WaitingStatus.class);
//3派送中
SendingStatus SENDING_STATUS = SpringUtil.getBean(SendingStatus.class);
//4已完成
FinishStatus FINISH_STATUS = SpringUtil.getBean(FinishStatus.class);
//5已取消
CanceledStatus CANCELED_STATUS = SpringUtil.getBean(CanceledStatus.class);
}
4.定义修改状态Service(状态模式环境类)
package com.practical.behaviorType.state.service;
import com.practical.behaviorType.state.domain.Orders;
import com.practical.behaviorType.state.orderStatus.OrdersStatus;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 状态模式环境类: 修改状态Service
*/
@Service
public class OrdersStatusService {
//用于存储所有状态, key: 状态值code value: 具体状态类的bean
private final static Map<Integer, OrdersStatus> map = new HashMap<>();
@Resource
private OrdersService ordersService;//用于操作数据库订单表
@Resource
private List<OrdersStatus> ordersStatusList;//注入所有OrdersStatus子类的bean
//在bean初始化后将所有具体状态放入map
@PostConstruct
private void init() {
for (OrdersStatus ordersStatus : ordersStatusList) {
map.put(ordersStatus.getCode(), ordersStatus);
}
}
//当前状态
private OrdersStatus ordersStatus;
private void setOrdersStatus(Integer status) {
this.ordersStatus = map.get(status);//设置环境的当前状态
this.ordersStatus.setOrdersStatusService(this);//指定状态类的环境
}
//从数据库查询当前状态
private Integer getStatus(Long orderId) {
return ordersService.lambdaQuery().select(Orders::getStatus).eq(Orders::getId, orderId).one().getStatus();
}
//付款操作 1待付款 -> 2待派送
public void pay(Long orderId) {
this.setOrdersStatus(this.getStatus(orderId));
ordersStatus.pay(orderId);
}
//派送操作 2待派送 -> 3派送中
public void send(Long orderId) {
this.setOrdersStatus(this.getStatus(orderId));
ordersStatus.send(orderId);
}
//送达操作 3派送中 -> 4已完成
public void delivery(Long orderId) {
this.setOrdersStatus(this.getStatus(orderId));
ordersStatus.delivery(orderId);
}
//取消操作 1待付款/2待派送 -> 5已取消
public void canceled(Long orderId) {
this.setOrdersStatus(this.getStatus(orderId));
ordersStatus.canceled(orderId);
}
}
5.测试
完全按照预期执行, 状态模式完成
总结
喜欢的朋友多多收藏点赞,如果对设计模式的实际运用感兴趣的人多, 我会继续更新更多的模式, 大家可以在评论区留言希望下一篇文章更新什么状态模式, 我从中挑取大家留言最多的模式优先码字, 欢迎留言。