原文地址:https://mp.weixin.qq.com/s?__biz=Mzg5NTEwNTk2MA==&mid=2247484973&idx=1&sn=324e26008102fa77dba0da71d2522ba2&chksm=c01428eaf763a1fc010bf449fae60e3f5bc2c891c5a35dfc606f322df84b3ddbe855bf322207&scene=132#wechat_redirect

概述

状态模式在日常开发中也是一个非常实用的行为型设计模式。

和以往一样,还是先来看看状态模式的定义:允许一个对象在其内部状态改变时改变它的行为,对象看起来似乎修改了它的类。好吧,我承认当初和你们一样,看到这句话的时候也是处于一个懵逼的状态,我们还是先别纠结了,继续往后面看。

使用场景

当你发现你的代码里面存在一个很长的if else列表,而这些分支都是因为不同状态下执行的操作不一样时考虑使用此模式。

还是举个例子,小二哥公司凭借强大的实力击败了众多的竞争者,成功获得了某个跨国连锁酒店的系统开发项目,需要开发一个app供用户使用,用户在该app上可以进行预定、退房等操作。这种情况比较适合用状态模式,房间有三种状态:1、空闲状态下,用户可以进行预定并将房间状态改为已预定状态;2、已预定状态下:用户可以选择入住将房间状态改为已入住状态(假设这里不考虑取消的情况);3、入住状态下:用户可以进行退房操作并重新将房间状态置为空闲状态。

再举个例子,我相信二十一世纪的你们应该都剁手用过某宝吧。某宝系统的订单系统就很适合使用状态模式来开发,这个过程中存在很多状态:待付款、待发货、待收货、待评价、退款/售后等等,订单位于不同的状态下的操作可能都不一样,例如待付款状态就需要买家进行付款;待收货状态就是商家通知物流系统准备发货等等。

UML类图

以上面酒店app为例,其UML图如下:

从上图可见,状态模式共有3个角色:

  • • HotelState:是一个接口,其封装了各个状态的统一操作;

  • • FreeTimeState等:HotelState的实现类,表示各个具体的状态;

  • • HotelContext: 保持并切换各个状态,其持有一个State的引用。它将依赖状态的各种操作委托给不同的状态对象执行。

状态模式实现

普通实现

我们先来看看不运用状态模式时的实现,如下:

class Hotel{
    public static final int FREEMTIME_STATE = 0;  //空闲状态
    public static final int BOOKED_STATE = 1;     //已预订状态
    public static final int CHECKIN_STATE = 2;    //入住状态

    private int state;

    public void setState(int state) {
        this.state = state;
    }

    public void Operate(){
        if (state == FREEMTIME_STATE){
            System.out.println("房间为空,预定该房间");
            state = BOOKED_STATE;
        }else if (state == BOOKED_STATE){
            System.out.println("房间已预定,入住该房间");
            state = CHECKIN_STATE;
        }else if (state == CHECKIN_STATE){
            System.out.println("房间已入住,退房");
            state = FREEMTIME_STATE;
        }
    }
}

public class StateExample1 {
    public static void main(String[] args) {
        Hotel hotel = new Hotel();

        hotel.setState(Hotel.FREEMTIME_STATE);
        hotel.Operate();

        hotel.setState(Hotel.BOOKED_STATE);
        hotel.Operate();

        hotel.setState(Hotel.CHECKIN_STATE);
        hotel.Operate();
    }
}

******************【运行结果】******************
房间为空,预定该房间
房间已预定,入住该房间
房间已入住,退房

我相信,看完上面的代码,你肯定觉得这样的代码是不好的,如果后面状态有扩展,会存在大量的if else。这样的代码非常不便于扩展和修改,好了,今天介绍的状态模式就派上用场了。

状态模式实现

下面看看状态模式下是如何实现上述功能。

enum HotelStateEnum {
    FREEMTIME_STATE,  //空闲状态
    BOOKED_STATE,    //已预订状态
    CHECKIN_STATE;   //入住状态
}

//1 此接口定义各个状态的统一操作接口
interface HotelState{
    void operate();
}

//2.1 实现各种状态类:空闲状态类
class FreeTimeState implements HotelState{
    private HotelStateEnum state;

    @Override
    public void operate() {
        System.out.println("房间为空,预定该房间");
        state = HotelStateEnum.BOOKED_STATE;
    }
}

//2.2 实现各种状态类:预定状态类
class BookedState implements HotelState{
    private HotelStateEnum state;

    @Override
    public void operate() {
        System.out.println("房间已预定,入住该房间");
        state = HotelStateEnum.CHECKIN_STATE;
    }
}

//2.3 实现各种状态类:入住状态类
class CheckinState implements HotelState{
    private HotelStateEnum state;

    @Override
    public void operate() {
        System.out.println("房间已入住,退房");
        state = HotelStateEnum.FREEMTIME_STATE;
    }
}

//定义一个酒店Context类
class HotelContext{
    private HotelState hotelState;

    public HotelState getHotelState() {
        return hotelState;
    }

    public void setHotelState(HotelState hotelState) {
        this.hotelState = hotelState;
    }

    public void doAction(){
        hotelState.operate();
    }
}


public class StateExample2 {
    public static void main(String[] args) {
        HotelContext hotelContext = new HotelContext();

        //空闲状态
        FreeTimeState freeTimeState = new FreeTimeState();
        hotelContext.setHotelState(freeTimeState);
        hotelContext.doAction();

        //预定状态
        BookedState bookedState = new BookedState();
        hotelContext.setHotelState(bookedState);
        hotelContext.doAction();

        //入住状态
        CheckinState checkinState = new CheckinState();
        hotelContext.setHotelState(checkinState);
        hotelContext.doAction();
    }
}

******************【运行结果】******************
房间为空,预定该房间
房间已预定,入住该房间
房间已入住,退房

可见,我们将每个状态下要做的具体动作封装到了每个状态类中,我们只需要切换不同的状态即可,便于扩展和修改。

状态模式特点

优点:增强了程序的可扩展性,因为我们很容易扩展一个新的State;增强了程序的封装性,每个状态的操作都被封装到了一个状态类中。

缺点:类变多了,这也是大部分设计模式通用的缺点了。

需要注意的是,如果看过前面秒懂策略模式一文的同学还有映像的话,就可能会觉得状态模式和策略模式特别相似,它们的UML类图几乎是一模一样的,但是我们不能被事物的表象所迷惑,如果我们深究这两种设计模式其背后的思想,就会发现它们的不同:

  • • 策略模式:定义了一组可互相代替的算法,这一组算法对象完成的是同一个任务,只是使用的方式不同,比如说我去西藏,我可以通过乘高铁实现;我也可以通过坐飞机实现。

  • • 状态模式:不同的状态完成的任务完全不一样。

本文源码地址:
https://github.com/qinlizhong1/javaStudy/tree/master/DesignPattern/src/state

 

更多推荐