我们先来假设一个业务场景来引入状态模式以便于更好的理解,以后的设计模式系列我都会以这样的方式来写。
我们在淘宝购买商品时,商品会依次出现以下状态:等待买家付款,买家已付款,送货中,交易成功。每个状态可以做的操作如下:
- 等待买家付款:付款(为了让整个流程可以走下去我们就忽略取消订单吧...);
- 买家已付款:退货;
- 送货中:退货,查看物流信息,确认收货;
- 交易成功:退货,查看物流信息,评价,再来一单;
- 退货成功:再来一单。
在这个场景下有很多显而易见的业务规则例如,商品状态是等待买家付款时是不能进行退货、查看物流信息、确认收货和评价操作的(事实上淘宝也不会给你这些选项);商品状态是买家已付款时不能做付款、查看物流信息、确认收货和评价操作等等。这些业务规则用if...else实现的效果是这样的:
public void 付款(){
if("等待买家付款".equals(state)){
state = "买家已付款";
System.out.println("付款成功!");
} else {
System.out.println("该商品已付款,不能重复付款!");
}
}
public void 退货(){
if("等待买家付款".equals(state)){
System.out.println("该商品还未付款,不能退货!");
} else if("退货成功".equals(state)){
System.out.println("该商品已退货,不能重复退货!");
} else {
state = "退货成功";
System.out.println("退货成功!");
}
public void 查看物流(){
if("等待买家付款".equals(state) || "买家已付款".equals(state)){
System.out.println("该商品还未发货,不能查看物流信息!");
} else {
System.out.println("该商品在路上遭遇车祸,被烧了...");
}
}
public void 评价(){
if("交易成功".equals(state)){
System.out.println("评价成功!");
} else {
System.out.println("交易未成功,不能评价!");
}
}
public void 再来一单(){
if("交易成功".equals(state) || "退货成功".equals(state)){
System.out.println("再次下单成功!");
} else {
System.out.println("我可没给你再来一单选项!");
}
}
我用手都敲酸了...那如果有10个状态呢?业务规则再复杂一些呢?用if...else耦合度太高,每次增加或修改一个状态或者业务规则就要修改每个方法的判断。并且随着业务复杂程度的提高,代码维护愈发困难。这种情况下,我们使用状态模式解决此业务场景更合适。
状态模式
状态模式即对象的内部状态改变时改变它的行为,看起来好像改变了它的类。状态模式是行为型模式的一种,后续我会介绍更多的行为型模式和其他类型的设计模式。
类图
从类图中我们可以看出状态模式中有三类角色,分别是:
- Context:环境角色,即拥有各个状态的对象;
- State:抽象状态接口角色,定义了Context对象拥有的全部状态的行为集合;
- ConcreteState:具体状态角色,对具体的状态定义了具体的行为。
实现思路
只看类图可能对状态模式的实现还不是这么的了解,我们把上面的例子代入类图各个角色就好理解一些。环境角色是拥有一系列状态的对象,在本例中也就是商品;抽象状态接口因为是抽象的并不对应任何对象;具体状态角色就包括了等待买家付款、买家已付款、送货中、交易成功和退货成功。
为了避免使用上面用if...else实现的高耦合的代码,商品对象便不能关心每一个状态的转换具体是怎么进行的,它应该只关心自己的当前状态(即保存当前状态的引用),状态间的转换交给具体状态角色去完成(具体状态角色知道与自己有关的状态转换过程)。
具体实现
抽象状态接口:
abstract class GoodsState {
//保存商品引用,用来修改商品状态
protected Goods goods;
public void setGoods(Goods goods) {
this.goods = goods;
}
//买家提交订单,商品状态变为“等待买家付款”
void submit(){};
//买家付款,商品状态变为“买家已付款”
void payment(){};
//卖家发货,商品状态变为“送货中”
void deliver(){};
//买家收货,商品状态变为“交易成功”
void receive(){};
//买家退货,商品状态变为“退货成功”
void giveBack(){};
}
商品状态类
class Goods {
//商品当前状态
private GoodsState currentState;
//等待买家付款
public static final WaitingForPayment WAITING_FOR_PAYMENT = new WaitingForPayment();
//买家已付款
public static final Paid PAID = new Paid();
//送货中
public static final Delivering DELIVERING = new Delivering();
//交易成功
public static final SuccessfulTrade SUCCESSFUL_TRADE = new SuccessfulTrade();
//退货成功
public static final SuccessfulReturn SUCCESSFUL_RETURN = new SuccessfulReturn();
Goods (GoodsState currentState) {
this.currentState = currentState;
this.currentState.setGoods(this);
}
public void submit() {
this.currentState.submit();
}
public void payment() {
this.currentState.payment();
}
public void deliver() {
this.currentState.deliver();
}
public void receive() {
this.currentState.receive();
}
public void giveBack() {
this.currentState.giveBack();
}
public void setCurrentState(GoodsState currentState){
this.currentState = currentState;
this.currentState.setGoods(this);
}
public GoodsState getCurrentState(){
return this.currentState;
}
}
:
//等待买家付款
class WaitingForPayment extends GoodsState{
@Override
public void submit() {
System.out.println("买家提交了订单,当前状态为:等待买家付款!");
}
@Override
public void payment() {
this.goods.setCurrentState(Goods.PAID);
this.goods.getCurrentState().payment();
}
@Override
public void deliver() {
System.out.println("你还没提交订单呢怎么可能给你送货呢?");
}
@Override
public void receive() {
System.out.println("你还没提交订单呢你咋接收到的?");
}
@Override
public void giveBack() {
System.out.println("你还没提交订单呢你退个鬼哦?");
}
}
//买家已付款
class Paid extends GoodsState{
@Override
public void submit() {
System.out.println("还要再来一件么老哥?");
}
@Override
public void payment() {
System.out.println("付款成功啦,等着卖家发货吧!");
}
@Override
public void deliver() {
this.goods.setCurrentState(Goods.DELIVERING);
this.goods.getCurrentState().deliver();
}
@Override
public void receive() {
System.out.println("你还没付钱呢你咋接收的?");
}
@Override
public void giveBack() {
System.out.println("你还没付钱呢你退个鬼哦?");
}
}
//送货中
class Delivering extends GoodsState{
@Override
public void submit() {
System.out.println("还要再来一件么老哥?");
}
@Override
public void payment() {
System.out.println("你已经付过钱了呀老哥?");
}
@Override
public void deliver() {
System.out.println("卖家发货咯!");
}
@Override
public void receive() {
this.goods.setCurrentState(Goods.SUCCESSFUL_TRADE);
this.goods.getCurrentState().receive();
}
@Override
public void giveBack() {
System.out.println("卖家还没发货呢你退个鬼哦?");
}
}
//交易成功
class SuccessfulTrade extends GoodsState{
@Override
public void submit() {
System.out.println("还要再来一件么老哥?");
}
@Override
public void payment() {
System.out.println("你已经付过钱了呀老哥?");
}
@Override
public void deliver() {
System.out.println("你都收到了还让卖家发货呢?");
}
@Override
public void receive() {
System.out.println("交易成功啦!");
}
@Override
public void giveBack() {
this.goods.setCurrentState(Goods.SUCCESSFUL_RETURN);
this.goods.getCurrentState().giveBack();
}
}
//退货成功
class SuccessfulReturn extends GoodsState{
@Override
public void submit() {
System.out.println("还要再来一件么老哥?");
}
@Override
public void payment() {
System.out.println("你已经付过钱了呀老哥?");
}
@Override
public void deliver() {
System.out.println("退货是你发货不是卖家发货哟?");
}
@Override
public void receive() {
System.out.println("你货都拿到手上了还收货呢?");
}
@Override
public void giveBack() {
System.out.println("老哥,退货成功了!");
}
}
商品类:
class Goods {
//商品当前状态
private GoodsState currentState;
//等待买家付款
public static final WaitingForPayment WAITING_FOR_PAYMENT = new WaitingForPayment();
//买家已付款
public static final Paid PAID = new Paid();
//送货中
public static final Delivering DELIVERING = new Delivering();
//交易成功
public static final SuccessfulTrade SUCCESSFUL_TRADE = new SuccessfulTrade();
//退货成功
public static final SuccessfulReturn SUCCESSFUL_RETURN = new SuccessfulReturn();
Goods (GoodsState currentState) {
this.currentState = currentState;
this.currentState.setGoods(this);
}
public void submit() {
this.currentState.submit();
}
public void payment() {
this.currentState.payment();
}
public void deliver() {
this.currentState.deliver();
}
public void receive() {
this.currentState.receive();
}
public void giveBack() {
this.currentState.giveBack();
}
public void setCurrentState(GoodsState currentState){
this.currentState = currentState;
this.currentState.setGoods(this);
}
public GoodsState getCurrentState(){
return this.currentState;
}
}
测试类:
public static void main(String[] args) {
Goods goods = new Goods(Goods.WAITING_FOR_PAYMENT);
goods.submit();
goods.payment();
goods.submit();
goods.giveBack();
goods.deliver();
goods.payment();
goods.receive();
goods.deliver();
goods.giveBack();
}
输出结果:
买家提交了订单,当前状态为:等待买家付款!
付款成功啦,等着卖家发货吧!
还要再来一件么老哥?
你还没付钱呢你退个鬼哦?
卖家发货咯!
你已经付过钱了呀老哥?
交易成功啦!
你都收到了还让卖家发货呢?
老哥,退货成功了!
虽然我冥思苦想了很久还是写出一个不是很完美的例子,但是作用还是达到了。这里需要注意的是在最开始设置状态和每次修改状态时我们都需要将当前商品对象传给商品状态对象,不然会出现空指针异常,同样,在商品类里每个状态都要初始化。
总结
通过以上我们可以看到使用状态模式避免了使用过多的if...else判断,使代码耦合度大幅度降低。但类的数目会随着状态数量的增加而增加,因此还需谨慎使用。
如果从这篇文章看过之前提到的一些设计原则可以发现状态模式符合几条设计原则,事实上每个设计模式都起码满足一条设计原则,其中最多的还是面向接口编程,读者可以在后续设计模式系列文章里留意。
最后,前几天加班有点严重,又因为无限火力开了所以这篇文章延后到现在才写完,真是惭愧。
引以为戒。