设计模式(10):状态模式

一、概念

1、定义:允许一个对象在其内部状态改变时,改变它的行为。状态模式允许对象再内部状态改变时改变它的行为,对象看起来好像修改了它的类。前半句话是说,这个模式将状态封装成了独立的类,并且将动作委托到当前状态的对象。行为会随着内部状态的改变而改变,糖果机投入前后的内部状态是不一样的。后半句话是说,以应用层视角来看,你使用的对象能够完全改变它的行为,你会觉得这个对象可能是从别的类实例化而来的,但实际上是引用不同状态来造成l类被改变的假象。
当控制一个对象状态转换的过程比较复杂的时候,可以把状态的判断逻辑转移到表示不同状态的一系列类当中,把复杂的判断逻辑简化,可以扩展到不同的类当中,扩展状态也相对容易。
2、类型:行为型

3、适用场景

  • 一个对象存在多个状态(不同状态下的行为不同,要看具体场景),且状态可以相互转换,在不同状态中判断是否可以转换到目标状态上

4、优缺点

  • 优点
    • 将不同的状态隔离
    • 把各种状态的转换逻辑,分布到State的子类中,减少相互依赖
    • 增加新的状态也简单
  • 缺点
    • 状态多的场景倒是类数目增加,系统变复杂

二、Coding

1、HeadFirst例子

HeadFirst糖果机例子
状态模式UML图

  • State:状态接口,定义了根据不同状态进行不同处理的接口,用来封装与上下文的一个特定状态所对应的行为
  • ConcreteState:具体实现状态处理的类,每个类实现一个跟上下文相关的状态的具体处理,上文即环境
  • Context:持有表示当前状态的ConcreteState角色。此外,它还定义了供外部调用者使用State模式的接口
    状态模式类图.png

2、视频场景

场景:网站的课程视频有暂停、播放、快进、停止,停止的时候无法快进,停止的状态转换成快进的时候需要做一层校验
在这里插入图片描述

//课程状态抽象类
public abstract class CourseVideoState {
    protected CourseVideoContext courseVideoContext;

    public void setCourseVideoContext(CourseVideoContext courseVideoContext) {
        this.courseVideoContext = courseVideoContext;
    }
    //四种状态,对应四种状态类,来继承CourseState抽象类
    public abstract void play();
    public abstract void speed();
    public abstract void pause();
    public abstract void stop();
}
//视频上下文Context
public class CourseVideoContext {

    //上下文组合课程视频的状态
    private CourseVideoState courseVideoState;
    //直接new成final,可以通过享元模式来共享同一个对象,所以声明为final
    public final static PlayState PLAY_STATE = new PlayState();
    public final static SpeedState SPEED_STATE= new SpeedState();
    public final static PauseState PAUSE_STATE = new PauseState();
    public final static StopState STOP_STATE = new StopState();

    //开放获取课程视频状态
    public CourseVideoState getCourseVideoState() {
        return courseVideoState;
    }

    public void setCourseVideoState(CourseVideoState courseVideoState) {
        this.courseVideoState = courseVideoState;
        //把自己设置成courseVideoState的上下文,这里是关键!!!
        this.courseVideoState.setCourseVideoContext(this);
    }

    //环境(上下文)想要play,直接调用状态的play
    public void play() {
        this.courseVideoState.play();
    }

    public void speed() {
        this.courseVideoState.speed();
    }

    public void pause() {
        this.courseVideoState.pause();
    }

    public void stop() {
        this.courseVideoState.stop();
    }
}

下面是四种具体状态

public class PlayState extends CourseVideoState{
    @Override
    public void play() {
        System.out.println("正常播放视频状态");
    }

    @Override
    public void speed() {
        //调用父类里边的上下文,把上下文里边的状态设置成快进,直接用之前声明的常量
        super.courseVideoContext.setCourseVideoState(CourseVideoContext.SPEED_STATE);
    }

    @Override
    public void pause() {
        super.courseVideoContext.setCourseVideoState(CourseVideoContext.PAUSE_STATE);
    }

    @Override
    public void stop() {
        super.courseVideoContext.setCourseVideoState(CourseVideoContext.STOP_STATE);
    }
}
public class SpeedState extends CourseVideoState {
    @Override
    public void play() {
        super.courseVideoContext.setCourseVideoState(CourseVideoContext.PLAY_STATE);
    }

    @Override
    public void speed() {
        System.out.println("快进播放视频状态");
    }

    @Override
    public void pause() {
        super.courseVideoContext.setCourseVideoState(CourseVideoContext.PAUSE_STATE);
    }

    @Override
    public void stop() {
        super.courseVideoContext.setCourseVideoState(CourseVideoContext.STOP_STATE);
    }
}
public class PauseState extends CourseVideoState {
    @Override
    public void play() {
        super.courseVideoContext.setCourseVideoState(CourseVideoContext.PLAY_STATE);
    }

    @Override
    public void speed() {
        super.courseVideoContext.setCourseVideoState(CourseVideoContext.SPEED_STATE);
    }

    @Override
    public void pause() {
        System.out.println("暂停播放视频状态");
    }

    @Override
    public void stop() {
        super.courseVideoContext.setCourseVideoState(CourseVideoContext.STOP_STATE);
    }
}
public class StopState extends CourseVideoState {
    @Override
    public void play() {
        super.courseVideoContext.setCourseVideoState(CourseVideoContext.PLAY_STATE);
    }

    @Override
    public void speed() {
        //停止时候不能快进
        System.out.println("ERROR!停止状态不能快进");
    }

    @Override
    public void pause() {
        System.out.println("ERROR!停止状态不能暂停");
    }

    @Override
    public void stop() {
        System.out.println("停止播放视频状态");

    }
}
public class Test {

    public static void main(String[] args) {
        CourseVideoContext courseVideoContext = new CourseVideoContext();
        courseVideoContext.setCourseVideoState(new PlayState());
        System.out.println("当前状态:"+courseVideoContext.getCourseVideoState().getClass().getSimpleName());
        //设置成暂停状态
        courseVideoContext.pause();
        System.out.println("当前状态:"+courseVideoContext.getCourseVideoState().getClass().getSimpleName());
        //设置成快进状态
        courseVideoContext.speed();
        System.out.println("当前状态:"+courseVideoContext.getCourseVideoState().getClass().getSimpleName());
        //设置成停止状态
        courseVideoContext.stop();
        System.out.println("当前状态:"+courseVideoContext.getCourseVideoState().getClass().getSimpleName());

        //从停止变快进
        courseVideoContext.speed();
        System.out.println("当前状态:"+courseVideoContext.getCourseVideoState().getClass().getSimpleName());
    }
}
当前状态:PlayState
当前状态:PauseState
当前状态:SpeedState
当前状态:StopState
ERROR!停止状态不能快进
当前状态:StopState

debug一下
调用的是PlayState的pause
互相持有引用
上下文中这一句很关键,把this状态设置到了上一行的课程视频状态中,这样的上下文就是最新的上下文了
没进入前,控制台看不到状态的上下文
进入后
互相引用

发布了43 篇原创文章 · 获赞 6 · 访问量 3907

猜你喜欢

转载自blog.csdn.net/weixin_44424668/article/details/103259962