初识设计模式,设计模式在js(ts)中的应用(五)

113 阅读3分钟

状态模式

在我们开发过程当中,我们经常会碰到这样一个场景,一个对象有多个状态,每次状态发生变化,他的行为或者逻辑就会发生改变,这个时候我们往往要面对复杂的判断逻辑。传统的方式是用if-else或者swtich-case把所有可能考虑到的情况都写进去,这样会导致代码很臃肿而去逻辑会越来越复杂。

定义

当对象改变状态时,其行为也跟着变化,看起来就像变了一个类。

状态模式的核心就是封装,状态发生变更引起了行为变化,从外部看就像是对象对应的类发生了变化一样。所以我们只需要关心他的状态。

例子

这边举一个电梯的例子,电梯一般有四个状态,运行、停止、开门、关门,这四个状态互相交织,互相有影响,比如开门的状态下就只允许关门的动作,而停止的状态下允许进行开门、关门或者运行的动作。如果采用传统的if-else判断的话会变成这样:

        let lift = {
            state: 'open',
            open: function() {
                if(this.state==='open'||this.state==='run'){
                    console.log('不允许开门')
                }else if(this.state==='stop'||this.state==='close'){
                    console.log('开门')
                }
            },
            close: function() {
                //...
            }
        }

这样不仅要重复做逻辑判断,会让代码十分臃肿。而且在将来添加状态后,会难以维护。

        /*电梯类*/
        class Lift {
            constructor() {
                this.openState = new OpenLiftState(this);
                this.closeState = new CloseLiftState(this);
                this.runState = new RunLiftState(this);
                this.stopState = new StopLiftState(this);
                this.nowState = this.stopState; // 初始的状态
            }
            open() {
                // 实际上调用的是状态类的方法
                this.nowState.open()
            }
            close() {
                this.nowState.close();
            }
            run() {
                this.nowState.run();
            }
            stop() {
                this.nowState.stop();
            }
        }
        /**
         * 电梯基本状态类
         * 提供电梯的基本状态下的方法
        */
        class LiftState {
            constructor(Lift) {
                // 状态类拿到电梯实例
                this.Lift = Lift;
            }
            /**
             * 状态改变方法
             * 把nowstate类直接变成对应状态类
            */
            setState(state){
                switch(state) {
                    case 'close': this.Lift.nowState = this.Lift.closeState; break;
                    case 'open': this.Lift.nowState = this.Lift.openState; break;
                    case 'run': this.Lift.nowState = this.Lift.runState; break;
                    case 'stop': this.Lift.nowState = this.Lift.stopState; break;
                }
            }
            open(){
                console.log('error:当前状态不允许开门');
            }
            run(){
                console.log('error:当前状态不允许运行');
            }
            close(){
                console.log('error:当前状态不允许关门');
            }
            stop(){
                console.log('error:当前状态不允许停止');
            }
        }
        class OpenLiftState extends LiftState {
            constructor(props){
                super(props)
            }
            /*开门的状态下只允许关门,所以我们重写开门的方法*/
            close() {
                console.log('已关门');
                this.setState('close');
            }
        }
        class CloseLiftState extends LiftState {
            constructor(props){
                super(props);
            }
            /*关门状态下允许开门、运行、停止*/
            open(){
                console.log('已开门');
                this.setState('open');
            }
            run(){
                console.log('已运行');
                this.setState('run');
            }
            stop(){
                console.log('已停止');
                this.setState('stop');
            }
        }
        class RunLiftState extends LiftState {
            constructor(props){
                super(props);
            }
            /*运行状态下只允许停止*/
            stop(){
                console.log('已停止');
                this.setState('stop');
            }
        }
        class StopLiftState extends LiftState {
            constructor(props){
                super(props);
            }
            /*停止状态下允许开门、运行*/
            open(){
                console.log('已开门');
                this.setState('open');
            }
            run(){
                console.log('已运行');
                this.setState('run');
            }
        }
        /*测试*/
        let lift = new Lift(); // 初始状态为stop
        lift.open(); // 开门
        lift.run(); // 不允许运行
        lift.stop(); // 不允许停止
        lift.close(); // 关门
        lift.stop(); // 停止

这样一来,我们只需要关注每个状态所能执行的动作,从而摆脱了复杂的逻辑判断。在将来,如果你想要添加一个新的状态,比如说故障,你就可以新增一个故障的状态类,这样就可以十分轻易的扩展你的类。

应用场景

组件的设计模式

在组件的开发过程当中,我们可以很好的应用到这个思想。当你的组件的行为受到状态的控制时,你就可以通过状态模式进行设计。这样设计出来的组件结构更清晰,也更易于扩展。但是这样的设计模式适合于状态较少的情况,当状态比较多的情况下,最好进行拆分成小组件后再用状态模式的思路进行设计。