二十二、状态模式

文章目录

  • [1 基本介绍](#1 基本介绍)
  • [2 案例](#2 案例)
    • [2.1 Season 接口](#2.1 Season 接口)
    • [2.2 Spring 类](#2.2 Spring 类)
    • [2.3 Summer 类](#2.3 Summer 类)
    • [2.4 Autumn 类](#2.4 Autumn 类)
    • [2.5 Winter 类](#2.5 Winter 类)
    • [2.6 Person 类](#2.6 Person 类)
    • [2.7 Client 类](#2.7 Client 类)
    • [2.8 Client 类的运行结果](#2.8 Client 类的运行结果)
    • [2.9 总结](#2.9 总结)
  • [3 各角色之间的关系](#3 各角色之间的关系)
    • [3.1 角色](#3.1 角色)
      • [3.1.1 State ( 状态 )](#3.1.1 State ( 状态 ))
      • [3.1.2 ConcreteState ( 具体的状态 )](#3.1.2 ConcreteState ( 具体的状态 ))
      • [3.1.3 Context ( 上下文 )](#3.1.3 Context ( 上下文 ))
      • [3.1.4 Client ( 客户端 )](#3.1.4 Client ( 客户端 ))
    • [3.2 类图](#3.2 类图)
  • [4 注意事项](#4 注意事项)
  • [5 优缺点](#5 优缺点)
  • [6 适用场景](#6 适用场景)
  • [7 总结](#7 总结)

1 基本介绍

状态模式 (State Pattern)是一种 行为型 设计模式,它 允许一个对象在其内部状态改变时改变它的行为 ,使得这个对象 看起来像是修改了它的类。

2 案例

本案例显示了 人 在 不同的季节 中 享受不同的假期 和 穿不同的上衣 的情况,季节的变化通过月份的变化而实现。

2.1 Season 接口

java 复制代码
public interface Season { // 季节
    String getName(); // 季节名称
    void festivals(); // 节日情况
    void dress(); // 上衣的穿着情况
}

2.2 Spring 类

java 复制代码
public class Spring implements Season { // 春季
	// 单例模式
    private static final Spring SPRING = new Spring();
    private Spring() {}
    public static Spring getInstance() {
        return SPRING;
    }

    @Override
    public String getName() {
        return "春季";
    }

    @Override
    public void festivals() {
        System.out.println("春节、元宵节、清明节。");
    }

    @Override
    public void dress() {
        System.out.println("从 棉袄 向 短袖 过渡。");
    }
}

2.3 Summer 类

java 复制代码
public class Summer implements Season { // 夏季
	// 单例模式
    private static final Summer SUMMER = new Summer();
    private Summer() {}
    public static Summer getInstance() {
        return SUMMER;
    }

    @Override
    public String getName() {
        return "夏季";
    }

    @Override
    public void festivals() {
        System.out.println("端午节、劳动节。");
    }

    @Override
    public void dress() {
        System.out.println("穿短袖。");
    }
}

2.4 Autumn 类

java 复制代码
public class Autumn implements Season { // 秋季
	// 单例模式
    private static final Autumn autumn = new Autumn();
    private Autumn() {}
    public static Autumn getInstance() {
        return autumn;
    }

    @Override
    public String getName() {
        return "秋季";
    }

    @Override
    public void festivals() {
        System.out.println("中秋节、重阳节。");
    }

    @Override
    public void dress() {
        System.out.println("从 短袖 向 棉袄 过渡。");
    }
}

2.5 Winter 类

java 复制代码
public class Winter implements Season { // 冬季
	// 单例模式
    private static final Winter INSTANCE = new Winter();
    private Winter() {}
    public static Winter getInstance() {
        return INSTANCE;
    }

    @Override
    public String getName() {
        return "冬季";
    }

    @Override
    public void festivals() {
        System.out.println("冬至、腊八节、除夕。");
    }

    @Override
    public void dress() {
        System.out.println("穿棉袄。");
    }
}

2.6 Person 类

java 复制代码
public class Person { // 人
    private int month; // 月份
    private Season season; // 月份对应的季节

    public void setMonth(int month) { // 设置月份和对应的季节
        if (month < 1 || month > 12) {
            throw new IllegalArgumentException("请输入正确的月份");
        }

        this.month = month;
        if (month >= 2 && month < 5) {
            season = Spring.getInstance();
        } else if (month >= 5 && month < 8) {
            season = Summer.getInstance();
        } else if (month >= 8 && month < 11) {
            season = Autumn.getInstance();
        } else {
            season = Winter.getInstance();
        }
    }

    public void showMonth() { // 显示月份和季节的情况
        System.out.println("=================="
                + month + "月是" + season.getName()
                + "==================");
    }

    public void showNotAttendClass() { // 显示不上课的情况,除了周末和寒暑假之外
        System.out.print("不上课的日子有:");
        season.festivals();
    }

    public void showDress() { // 显示上衣的穿着情况
        System.out.print("上衣的穿着情况:");
        season.dress();
    }
}

2.7 Client 类

java 复制代码
public class Client { // 客户端,测试了 人在每个月 不上课的情况 和 上衣的穿着情况
    public static void main(String[] args) {
        Person person = new Person();
        // 每隔 1 秒增加 1 个月
        for (int month = 1; month <= 12; month++) {
            person.setMonth(month);
            person.showMonth();
            person.showNotAttendClass();
            person.showDress();
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
}

2.8 Client 类的运行结果

复制代码
==================1月是冬季==================
不上课的日子有:冬至、腊八节、除夕。
上衣的穿着情况:穿棉袄。
==================2月是春季==================
不上课的日子有:春节、元宵节、清明节。
上衣的穿着情况:从 棉袄 向 短袖 过渡。
==================3月是春季==================
不上课的日子有:春节、元宵节、清明节。
上衣的穿着情况:从 棉袄 向 短袖 过渡。
==================4月是春季==================
不上课的日子有:春节、元宵节、清明节。
上衣的穿着情况:从 棉袄 向 短袖 过渡。
==================5月是夏季==================
不上课的日子有:端午节、劳动节。
上衣的穿着情况:穿短袖。
==================6月是夏季==================
不上课的日子有:端午节、劳动节。
上衣的穿着情况:穿短袖。
==================7月是夏季==================
不上课的日子有:端午节、劳动节。
上衣的穿着情况:穿短袖。
==================8月是秋季==================
不上课的日子有:中秋节、重阳节。
上衣的穿着情况:从 短袖 向 棉袄 过渡。
==================9月是秋季==================
不上课的日子有:中秋节、重阳节。
上衣的穿着情况:从 短袖 向 棉袄 过渡。
==================10月是秋季==================
不上课的日子有:中秋节、重阳节。
上衣的穿着情况:从 短袖 向 棉袄 过渡。
==================11月是冬季==================
不上课的日子有:冬至、腊八节、除夕。
上衣的穿着情况:穿棉袄。
==================12月是冬季==================
不上课的日子有:冬至、腊八节、除夕。
上衣的穿着情况:穿棉袄。

2.9 总结

本案例让四个季节对应四个类,并且都实现了 Season 接口,从而能够通过调用 Season 的接口来动态地调用具体季节的方法,很好地利用了面向对象的 多态性,避免了很多判断分支语句,使得编写代码时更简单。

此外,由于四个具体的季节类中没有成员字段,所以使用 单例模式 来创建它们的对象实例。

3 各角色之间的关系

3.1 角色

3.1.1 State ( 状态 )

该角色负责 定义 根据不同状态进行不同处理的 接口 。本案例中,Season 接口扮演了该角色。

3.1.2 ConcreteState ( 具体的状态 )

该角色负责 实现 State 角色定义的 接口 。本案例中,Spring, Summer, Autumn, Winter 类都在扮演该角色。

3.1.3 Context ( 上下文 )

该角色负责 持有表示当前状态的 ConcreteState 角色的实例对象 ,并 定义供外部使用的接口 。本案例中,Person 类扮演了该角色。

3.1.4 Client ( 客户端 )

该角色负责 使用 Context 角色完成具体的业务逻辑 。本案例中,Client 类扮演了该角色。

3.2 类图

说明:

  • 有时候 State 可以使用抽象类实现,只需要注意 单继承 即可。
  • Context 表面上聚合了 State,实际上根据具体情况会聚合不同的 ConcreteState。
  • Client 可能不会直接使用 State 和 ConcreteState ,而是给 Context 传递一个变量,Context 根据这个变量修改自身的状态,就像本案例一样,Client 类给 Person 类的 setMonth() 方法传递 月份 这个参数,从而修改 Person 类内部聚合的 season 对象。

4 注意事项

  • 避免过多状态 :虽然状态模式可以处理多个状态,但过多的状态会使系统变得复杂,增加维护难度。因此,在设计时应尽量控制状态的数量,避免状态爆炸。
  • 遵守单一职责原则:每个 ConcreteState 应该只负责一种状态的行为,避免将多个状态的行为放在同一个类中。
  • 避免复杂逻辑:ConcreteState 中的逻辑应该尽量简单,避免在 ConcreteState 中实现复杂的业务逻辑。复杂的逻辑应该由 Context 或其他类来处理。
  • 性能考虑 :虽然状态模式可以提高代码的可读性和可维护性,但在某些情况下,由于 需要频繁地创建和销毁状态对象 ,可能会降低系统的性能 。因此,应该避免不必要的对象创建和销毁,可以考虑使用 单例模式 、享元模式 来避免创建过多的对象。
  • 状态转换的管理 :状态转换有两种管理方式,在使用本模式时需要选择具体的管理方式。
    • 在 Context 中管理 :
      • 优点:提高 ConcreteState 的独立性,程序的整体结构会更加清晰。
      • 缺点:Context 必须了解所有 ConcreteState。
    • 在各个 ConcreteState 中管理 :
      • 优点:每个 ConcreteState 都知道在什么情况下进行状态转换。
      • 缺点:每个 ConcreteState 都必须了解所有 ConcreteState。

5 优缺点

优点:

  • 提高代码的可读性和可重用性 :状态模式通过明确的 ConcreteState 来表示不同的状态,使得代码更加易于理解和重用。其他对象或系统也可以通过 State 与 ConcreteState 进行交互,而不需要关心具体的状态实现,从而提高了代码的 可读性 和 可重用性。
  • 增强系统的可维护性 :由于每个 ConcreteState 都封装了与特定状态相关的行为,因此当需要修改某个状态的行为时,只需要修改该 ConcreteState 的代码即可,而不会影响到其他 ConcreteState 或 Context 的代码,从而增强了系统的 可维护性。
  • 增强系统的扩展性 :当需要添加新的状态时,只需要新增一个 ConcreteState,并在 Context 中修改状态转换的逻辑即可,而不需要修改其他 ConcreteState 的代码,这符合开闭原则(对扩展开放,对修改关闭),从而增强了系统的 扩展性。

缺点:

  • 增加类的数量:状态模式会引入大量的 ConcreteState,这可能会增加系统的复杂性和类的数量。当状态数量较多时,系统的维护成本也会相应增加。
  • 状态转换的逻辑可能变得复杂 :在某些情况下,状态之间的转换逻辑可能非常复杂,这可能会导致 ConcreteState 的代码变得难以理解和维护。此外,如果状态转换的逻辑不恰当,还可能导致系统出现错误或不一致的行为。
  • 对开闭原则的支持有限 :虽然状态模式在一定程度上支持开闭原则,但在某些情况下,添加 新的状态 或 修改状态转换的逻辑 仍然需要修改 ConcreteState 的代码,这可能会违反开闭原则,使得系统的扩展性受到一定的限制。

6 适用场景

  • 多状态行为 :当 一个对象具有多种状态 ,并且这些状态会影响其行为 时,可以使用状态模式。每个状态对应一个 ConcreteState,封装了在该状态下对象的行为。
  • 状态转换复杂 :如果 对象的状态转换逻辑非常复杂 ,并且这些转换逻辑分散在多个地方(如多个条件语句或方法中),那么使用状态模式可以将这些逻辑集中管理,使代码更加清晰和易于维护。
  • 实现状态机 :状态模式是实现 状态机 的一种有效方式。状态机是 一种用于描述系统在不同状态下如何响应不同事件的模型。状态模式允许将状态机的各个状态作为独立的类来实现,并通过状态转换来模拟状态机的行为。

7 总结

状态模式 是一种 行为型 设计模式,它使用 多态性 来区分对象在不同状态下的不同行为,避免了在多处书写多重分支语句的复杂性。在本模式中,对象的 状态转换 是一个重点,使用本模式之前需要明确对象的状态应该在合适变化。本模式在生产中比较常用,因为现实生活中有很多实体需要有状态的概念,例如用户的标签。

相关推荐
xcl09255 小时前
北京24小时自助健身房系统软件开发实战:从架构到部署全流程指南
java·spring boot·架构
一 乐6 小时前
动漫书销售商城|基于springboot + vue动漫书销售商城(源码+数据库+文档)
java·数据库·vue.js·spring boot·毕业设计
卓怡学长7 小时前
w214基于jsp知道特产网
java·intellij-idea
步行cgn7 小时前
Spring 注解使用详解
java·spring
一条小小yu7 小时前
Spring IoC的理解
java·后端·spring
落魄实习生7 小时前
Agent Scope Java 2.x 系列【7】工具使用
java·开发语言·ai
旺仔学长 哈哈7 小时前
springboot钓鱼爱好者交流平台APP设计与实现
java·spring boot·mysql·充电桩管理系统
自强的小白9 小时前
核心功能(Service接口)
java·mybatis
乌暮10 小时前
深入理解 Java 泛型:把「万能盒子」用对、用稳
java·开发语言·后端·学习