利用枚举简单实现状态模式

73 阅读3分钟

话不多说,上代码。以TCP连接建立和释放为例。

Event 枚举

public enum Event {
    TRY_TO_CONNECT,
    RECEIVE_SYN,
    RECEIVE_SYN_AND_ACK,
    RECEIVE_ACK,
    TRY_TO_CLOSE,
    RECEIVE_FIN_1,
    RECEIVE_FIN_1_ACK,
    TRY_TO_LAST_CLOSE,
    RECEIVE_FIN_2,
    RECEIVE_FIN_2_ACK,
    PERIOD_WAIT_2

}

State 枚举

public enum State {
    LISTEN{
        @Override
        public void handler(TCPObject tcpObject, Event event) throws Exception {
            switch (event) {
                case RECEIVE_SYN -> tcpObject.setState(SYN_RECEIVED);
                default -> throw new Exception("illegal state");
            }
            System.out.println(tcpObject);
        }
    },
    SYN_SENT {
        @Override
        public void handler(TCPObject tcpObject, Event event) throws Exception {
            switch (event) {
                case RECEIVE_SYN_AND_ACK -> tcpObject.setState(ESTABLISHED);
                default -> throw new Exception("illegal state");
            }
            System.out.println(tcpObject);
        }
    },
    SYN_RECEIVED {
        @Override
        public void handler(TCPObject tcpObject, Event event) throws Exception {
            switch (event) {
                case RECEIVE_ACK -> tcpObject.setState(ESTABLISHED);
                default -> throw new Exception("illegal state");
            }
            System.out.println(tcpObject);
        }
    },
    ESTABLISHED {
        @Override
        public void handler(TCPObject tcpObject, Event event) throws Exception {
            switch (event) {
                case TRY_TO_CLOSE -> tcpObject.setState(FIN_WAIT_1);
                case RECEIVE_FIN_1 -> tcpObject.setState(CLOSE_WAIT);
                default -> throw new Exception("illegal state");
            }
            System.out.println(tcpObject);
        }
    },
    FIN_WAIT_1 {
        @Override
        public void handler(TCPObject tcpObject, Event event) throws Exception {
            switch (event) {
                case RECEIVE_FIN_1_ACK -> tcpObject.setState(FIN_WAIT_2);
                default -> throw new Exception("illegal state");
            }
            System.out.println(tcpObject);
        }
    },
    FIN_WAIT_2 {
        @Override
        public void handler(TCPObject tcpObject, Event event) throws Exception {
            switch (event) {
                case RECEIVE_FIN_2 -> tcpObject.setState(TIME_WAIT);
                default -> throw new Exception("illegal state");
            }
            System.out.println(tcpObject);
        }
    },
    CLOSE_WAIT {
        @Override
        public void handler(TCPObject tcpObject, Event event) throws Exception {
            switch (event) {
                case TRY_TO_LAST_CLOSE -> tcpObject.setState(LAST_ACK);
                default -> throw new Exception("illegal state");
            }
            System.out.println(tcpObject);
        }
    },
    LAST_ACK {
        @Override
        public void handler(TCPObject tcpObject, Event event) throws Exception {
            switch (event) {
                case RECEIVE_FIN_2_ACK -> tcpObject.setState(CLOSED);
                default -> throw new Exception("illegal state");
            }
            System.out.println(tcpObject);
        }
    },
    TIME_WAIT {
        @Override
        public void handler(TCPObject tcpObject, Event event) throws Exception {
            switch (event) {
                case PERIOD_WAIT_2 -> tcpObject.setState(CLOSED);
                default -> throw new Exception("illegal state");
            }
            System.out.println(tcpObject);
        }
    },
    CLOSED {
        @Override
        public void handler(TCPObject tcpObject, Event event) throws Exception {
            switch (event) {
                case TRY_TO_CONNECT -> tcpObject.setState(SYN_SENT);
                default -> throw new Exception("illegal state");
            }
            System.out.println(tcpObject);
        }
    };

    public abstract void handler(TCPObject tcpObject,Event event) throws Exception;

    public static State findState(TCPObject tcpObject) throws Exception {
        for (State value : State.values()) {
            if (value == tcpObject.getState()) {
                return value;
            }
        }
        throw new Exception("error");
    }
}

TCP对象类

public class TCPObject {
    private State state;
    private String name;

    public TCPObject(State state,String name) {
        this.state = state;
        this.name = name;
    }

    public State getState() {
        return state;
    }

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

    @Override
    public String toString() {
        return name + "'s state is " + state.name() + "\n" + "---------------------------";
    }

    public static void main(String[] args) throws Exception {
        TCPObject server = new TCPObject(State.LISTEN,"server");
        TCPObject client = new TCPObject(State.CLOSED,"client");
        System.out.println(server);
        System.out.println(client);


        State.findState(client).handler(client, Event.TRY_TO_CONNECT);
        State.findState(server).handler(server, Event.RECEIVE_SYN);
        State.findState(client).handler(client, Event.RECEIVE_SYN_AND_ACK);
        State.findState(server).handler(server, Event.RECEIVE_ACK);

        State.findState(client).handler(client, Event.TRY_TO_CLOSE);
        State.findState(server).handler(server, Event.RECEIVE_FIN_1);
        State.findState(client).handler(client, Event.RECEIVE_FIN_1_ACK);
        State.findState(server).handler(server, Event.TRY_TO_LAST_CLOSE);
        State.findState(client).handler(client, Event.RECEIVE_FIN_2);
        State.findState(server).handler(server, Event.RECEIVE_FIN_2_ACK);
        State.findState(client).handler(client, Event.PERIOD_WAIT_2);


    }
}

输出

server's state is LISTEN
---------------------------
client's state is CLOSED
---------------------------
client's state is SYN_SENT
---------------------------
server's state is SYN_RECEIVED
---------------------------
client's state is ESTABLISHED
---------------------------
server's state is ESTABLISHED
---------------------------
client's state is FIN_WAIT_1
---------------------------
server's state is CLOSE_WAIT
---------------------------
client's state is FIN_WAIT_2
---------------------------
server's state is LAST_ACK
---------------------------
client's state is TIME_WAIT
---------------------------
server's state is CLOSED
---------------------------
client's state is CLOSED
---------------------------

Process finished with exit code 0

总结

TCP链接的建立和释放肯定不会只有以上几种情况,但用来描述状态模式我想已经足够了。以我个人看法,状态模式的精华在于状态、事件、行为这三种元素。某个状态接收到某种事件后执行相应的行为便能跳转到下一状态。在实际应用中,关键是要识别出状态和事件。确定好状态和事件后,行为则由具体的业务逻辑决定,在此样例中,各个枚举的handler方法内还欠缺了具体的逻辑,比如发送SYN包等。这些具体的逻辑可以通过一个专门的工具类内的各个逻辑处理方法进行封装。 用枚举实现有一个好处,就是当状态越来越多时,不会出现类爆炸的现象,缺点则是不符合开闭原则。

本人只是一个刚干活1年的小菜鸡,欢迎各位大佬指点指点~