学过UML建模的人一定会状态图不陌生,每个节点代表一个状态,节点与节点之间的连线代表触发状态转移的事件。
上面是一个简化的订单流转的状态图,从图中可以看到除了开始、终止节点外,我们还有6个状态:待支付、待发货、待收货/提货、待评价、待结单;每个状态之间有一个箭头标识的连线,上面写着能够触发订单状态流转到下一个状态的事件。
如果没有设计模式,我们应该怎么去开发上述订单流转的代码?我们一起来看看
public class OrderStateMachine{
/**
* 订单支付
*/
public void pay(Order order){
if(order.state == '待支付'){
//TODO 去支付,修改订单状态为【待发货】
} else if(order.state == '待发货'){
//TODO 提示“请先支付”
} else if(order.state == '待收货'){
//TODO 提示“请先支付”
} else if(order.state == '待评价'){
//TODO 提示“请先支付”
} else if(order.state == '待接单'){
//TODO 提示“请先支付”
} else if(order.state == '售后'){
//TODO 提示“请先支付”
}
}
/**
* 订单发货
*/
public void delivery(Order order){
if(order.state == '待支付'){
//TODO 提示“请勿重复支付”
} else if(order.state == '待发货'){
//TODO 去发货,修改订单状态为【待收货】
} else if(order.state == '待收货'){
//TODO 提示“未发货”
} else if(order.state == '待评价'){
//TODO 提示“未发货”
} else if(order.state == '待结单'){
//TODO 提示“未发货”
} else if(order.state == '售后'){
//TODO 提示“未发货”
}
}
……
//TODO 收货、评价、结单、售后
}
这段代码也能实现状态流转的功能,但是这段代码存在什么问题呢?
1.不满足开放闭合原则,增加一个新的订单状态,所有的方法都需要增加新的判断分支,从而引入了更多的不确定性,加大了测试工作量。
2.代码结构混乱,如果订单状态越来越多,上述代码量会越来越大,代码中到处都散布着if else判断逻辑,不方便代码阅读。
下面我们引入状态模式来看看改造后的代码
public abstract class OrderStateMachine{
/**
* 订单发货
*/
public void pay(Order order){
//TODO 提示操作非法
}
/**
* 订单发货
*/
public void delivery(Order order){
//TODO 提示操作非法
}
……
//TODO 收货、评价、结单、售后
}
/**
* 订单支付状态流转
*/
public class OrderPayState{
public void pay(Order order){
//TODO 去支付,修改订单状态为【待发货】
}
/**
* 如果需要自定义提示,就需要重写这个方法。否则使用父类中给出的提示信息
*/
public void delivery(Order order){
//TODO 结合具体情况给出提示
}
}
/**
* 订单发货状态流转
*/
public class OrderDeliveryState{
/**
* 如果需要自定义提示,就需要重写这个方法。否则使用父类中给出的提示信息
*/
public void pay(Order order){
//TODO 结合具体情况给出提示
}
public void delivery(Order order){
//TODO 去发货,修改订单状态为【待收货】
}
}
下面我们来看看上段代码中出现的2个问题是否还会出现
1.不满足开放闭合原则
假设新增一个状态,我们最少的改动是:在OrderStateMachine中增加一个方法,新增一个状态类。
2.代码结构混乱
假设新增一个状态,新增的状态涉及到的业务逻辑都被封装到新的状态类中,既避免了第一版中的代码过长的问题,又便于阅读和排查问题。
那么什么是状态模式呢?
官方给出的解释:是一种行为设计模式, 让你能在一个对象的内部状态变化时改变其行为, 使其看上去就像改变了自身所属的类一样。
对于新手来说可能无法理解上面这句话,那我们可以借助例子去记忆。我们在上面编写的订单状态流转就是一个不错的例子
1.首先有个对象贯穿整个流程,这个对象就是一个实例化的订单,它包含订单状态,这个订单状态存在多种情况,有流转
2.对象状态能够通过某些指定的事件触发从而让它转移到另一个状态