鸣谢:《大话设计模式》
例子来源于慕课视频《Java设计模式精讲 Debug方式+内存分析》

简介

是一种行为型设计模式,用于允许一个对象在其内部状态改变时改变它的行为。状态模式将一个对象的状态封装在不同的状态类中,使对象能够根据当前状态执行不同的操作。这使得对象的行为可以根据状态的变化而变化,而不需要大量的条件语句。

主要组成部分:

  • 上下文(Context):上下文是包含状态的对象,它维护一个对当前状态的引用,并且根据当前状态执行相应的操作。上下文是客户端与状态类交互的接口。

  • 抽象状态(State):抽象状态是状态类的抽象,它定义了一个接口,用于封装与上下文的交互。抽象状态类通常包括了所有可能的状态转换的方法。

  • 具体状态(Concrete State):具体状态是抽象状态的具体实现,它实现了状态接口中定义的方法。不同的具体状态类负责不同的状态行为。

工作流程

这个是chatgpt生成的。总结的很好,尤其是第三点。有助于对状态模式的理解

  • 上下文对象维护一个对当前状态的引用。

  • 当上下文需要执行操作时,它调用当前状态对象的相应方法。

  • 当状态发生变化时,上下文可以切换到新的状态,从而改变它的行为。


例子

结构

代码

上下文(Context)

/**
 * @author: lpy
 * @Date: 2023/10/26
 */
public class VideoContext {
    public VideoState videoState;

    public static final PlayState PLAY_STATE = new PlayState();
    public static final StopState STOP_STATE = new StopState();
    public static final PausedState PAUSED_STATE = new PausedState();
    public static final SpeedState SPEED_STATE = new SpeedState();

    public VideoState getVideoState() {
        return videoState;
    }

    public void setVideoState(VideoState videoState) {
        this.videoState = videoState;
        // 因为继承自抽象类的playstate,pausedState等类在设置super.videoContext.setVideoState的时候是需要用到的
        this.videoState.setVideoContext(this);
    }

    public void play(){
        this.videoState.play();
    }
    public void stop(){
        this.videoState.stop();
    }
    public void paused(){
        this.videoState.paused();
    }
    public void speed(){
        this.videoState.speed();
    }
}

抽象状态(State)

/**
 * @author: lpy
 * @Date: 2023/10/26
 */
public abstract class VideoState {
    protected VideoContext videoContext;

    public void setVideoContext(VideoContext v){
        this.videoContext = v;
    }

    // 这几个方法一定是抽象的
    public abstract void play();
    public abstract void stop();
    public abstract void speed();
    public abstract void paused();
}

具体状态(Concrete State)

/**
 * @author: lpy
 * @Date: 2023/10/26
 */
public class PlayState extends VideoState{
    @Override
    public void play() {
        System.out.println("正常播放视频");
    }

    @Override
    public void stop() {
        super.videoContext.setVideoState(VideoContext.STOP_STATE);
    }

    @Override
    public void speed() {
        super.videoContext.setVideoState(VideoContext.SPEED_STATE);
    }

    @Override
    public void paused() {
        super.videoContext.setVideoState(VideoContext.PAUSED_STATE);

    }
}
/**
 * @author: lpy
 * @Date: 2023/10/26
 */
public class SpeedState extends VideoState{
    @Override
    public void play() {
        super.videoContext.setVideoState(VideoContext.PLAY_STATE);
    }

    @Override
    public void stop() {
        System.out.println("正常播放");
    }

    @Override
    public void speed() {
        super.videoContext.setVideoState(VideoContext.SPEED_STATE);
    }

    @Override
    public void paused() {
        super.videoContext.setVideoState(VideoContext.PAUSED_STATE);
    }
}
/**
 * @author: lpy
 * @Date: 2023/10/26
 */
public class StopState extends VideoState{
    @Override
    public void play() {
        super.videoContext.setVideoState(VideoContext.PLAY_STATE);
    }

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

    @Override
    public void speed() {
        System.out.println("error:已停止视频无法倍速");
    }

    @Override
    public void paused() {
        System.out.println("error: 已停止视频无法暂停");
    }
}
/**
 * @author: lpy
 * @Date: 2023/10/26
 */
public class PausedState extends VideoState{
    @Override
    public void play() {
        super.videoContext.setVideoState(VideoContext.PLAY_STATE);
    }

    @Override
    public void stop() {
        super.videoContext.setVideoState(VideoContext.STOP_STATE);
    }

    @Override
    public void speed() {
        System.out.println("error: 暂停状态无法倍速");
    }

    @Override
    public void paused() {
        super.videoContext.setVideoState(VideoContext.PAUSED_STATE);
    }
}

测试类

/**
 * @author: lpy
 * @Date: 2023/10/26
 */
public class StateModeTest {
    public static void main(String[] args) {
        VideoContext videoContext = new VideoContext();
        videoContext.setVideoState(new PlayState());
        System.out.println(videoContext.getVideoState().getClass().getSimpleName());
        // 有读者说“你难道没发现,真正的目标方法内的语句并没有输出吗“,可以看这个语句,以及输出,就明白了
        videoContext.play();
        System.out.println("---------1-----------");

        System.out.println("---------2-----------");
        videoContext.stop();
        System.out.println(videoContext.getVideoState().getClass().getSimpleName());
        System.out.println("---------2-----------");

        System.out.println("---------3-----------");
        videoContext.paused();
        System.out.println(videoContext.getVideoState().getClass().getSimpleName());
        System.out.println("---------3-----------");

        System.out.println("---------4-----------");
        videoContext.stop();
        System.out.println(videoContext.getVideoState().getClass().getSimpleName());
        System.out.println("---------4-----------");

        videoContext.speed();
        System.out.println(videoContext.getVideoState().getClass().getSimpleName());
    }
}
// 输出如下
/**
PlayState
正常播放视频
---------1-----------
---------2-----------
StopState
---------2-----------
---------3-----------
error: 已停止视频无法暂停
StopState
---------3-----------
---------4-----------
停止播放视频
StopState
---------4-----------
error:已停止视频无法倍速
StopState
*/

应用

  • 订单处理系统:在订单处理系统中,订单可以处于多个状态,如已下单、已付款、已发货、已完成等。状态模式可以用于管理订单状态转换和相应的处理步骤。

  • 网络连接管理:在网络编程中,网络连接可以处于多个状态,如连接中、已连接、已断开等。状态模式可用于管理网络连接的状态和相应的处理。

  • 游戏开发:在游戏开发中,状态模式可以用来管理游戏中不同角色或游戏对象的状态,例如,角色可以处于正常状态、受伤状态、死亡状态等。每种状态下角色的行为和动作都是不同的。

状态模式优缺点

  • 优点:

    • 分离关注点:状态模式将每个状态的行为封装在不同的状态类中,使代码更加清晰和易于维护。它分离了状态的转换和状态的行为,使系统更具灵活性。

    • 开闭原则:向状态模式添加新的状态很容易,因为它不需要修改现有的状态类或上下文类,符合开闭原则。

    • 消除条件语句:状态模式消除了大量的条件语句,因为状态的行为由状态类自身管理,而不需要在上下文中编写复杂的条件逻辑。

  • 缺点:

    • 增加了类的数量:状态模式引入了多个状态类,这可能导致类的数量增加,使类的层次结构变得更加复杂。对于小型项目,这可能会显得过度繁琐。

    • 增加了代码复杂性:虽然状态模式可以减少条件语句,但在某些情况下可能会增加代码的复杂性。状态之间的转换逻辑可能会分散在不同的状态类中,需要更多的理解和调试。

    • 不适用于简单情况:状态模式主要用于管理复杂对象的状态变化,对于简单对象,引入状态模式可能会显得过于繁琐,不划算。

    • 可能导致过度设计:在某些情况下,状态模式可能会导致过度设计,特别是当应用中只有很少几种状态时。过度使用状态模式可能会使代码不必要地复杂。

    • 难以维护状态的一致性:如果状态之间存在复杂的依赖关系,确保状态之间的一致性可能会变得困难。状态模式并不处理状态之间的相互作用,因此这些关系需要在应用中小心管理。

7 大设计原则

不能全部遵守 , 也不能不遵守 , 注意平衡 功能 和 系统复杂度 , 找到最合适的一个点 ;

- 单一职责原则(Single Responsibility Principle - SRP):
这个原则规定一个类应该只有一个改变的理由,也就是说,一个类应该只有一个单一的职责。这有助于保持类的简单性和可维护性,因为每个类只需关注一个特定的功能。

- 开放-封闭原则(Open-Closed Principle - OCP):
开放-封闭原则要求系统中的软件实体(类、模块、函数等)应该对扩展开放,对修改封闭。这意味着当需要添加新功能时,应该通过扩展现有代码来实现,而不是修改原有代码。

- 里氏替换原则(Liskov Substitution Principle - LSP):
里氏替换原则规定,子类应该能够替换其父类,而不会影响程序的正确性。也就是说,子类应该继承父类的行为,但可以扩展或修改该行为,但不应该改变父类的行为。

- 接口隔离原则(Interface Segregation Principle - ISP):
接口隔离原则要求一个类不应该强迫其客户端依赖于它们不需要的接口。应该根据客户端的需求定义小而精确的接口,而不是大而笨重的接口。

- 依赖倒置原则(Dependency Inversion Principle - DIP):
依赖倒置原则要求高级模块不应该依赖于低级模块,它们都应该依赖于抽象。具体来说,这意味着应该通过抽象接口或抽象类来定义模块之间的依赖关系,而不是直接依赖于具体实现。

- 迪米特法则(Law of Demeter - LoD):
如果两个类不必彼此直接通信,那么这两个类就不应当发生直接的相互作用。如果其中一个类需要调用另一个类的某一个方法的话,可以通过第三者转发这个调用。

- 合成复用原则(Composite Reuse Principle - CRP):
合成复用原则鼓励通过组合(合成)已有的类来实现新功能,而不是通过继承现有的类。这样可以降低系统的耦合度,并且更加灵活。

源码位置

全部源码github.com/xs-alpha/designPattern

参考文献

如有内容侵权,麻烦联系博主删除

浙公网安备33011302000604

辽ICP备20003309号