二十二、状态模式

打印 上一主题 下一主题

主题 537|帖子 537|积分 1611


1 基本介绍

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

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

  1. public interface Season { // 季节
  2.     String getName(); // 季节名称
  3.     void festivals(); // 节日情况
  4.     void dress(); // 上衣的穿着情况
  5. }
复制代码
2.2 Spring 类

  1. public class Spring implements Season { // 春季
  2.         // 单例模式
  3.     private static final Spring SPRING = new Spring();
  4.     private Spring() {}
  5.     public static Spring getInstance() {
  6.         return SPRING;
  7.     }
  8.     @Override
  9.     public String getName() {
  10.         return "春季";
  11.     }
  12.     @Override
  13.     public void festivals() {
  14.         System.out.println("春节、元宵节、清明节。");
  15.     }
  16.     @Override
  17.     public void dress() {
  18.         System.out.println("从 棉袄 向 短袖 过渡。");
  19.     }
  20. }
复制代码
2.3 Summer 类

  1. public class Summer implements Season { // 夏季
  2.         // 单例模式
  3.     private static final Summer SUMMER = new Summer();
  4.     private Summer() {}
  5.     public static Summer getInstance() {
  6.         return SUMMER;
  7.     }
  8.     @Override
  9.     public String getName() {
  10.         return "夏季";
  11.     }
  12.     @Override
  13.     public void festivals() {
  14.         System.out.println("端午节、劳动节。");
  15.     }
  16.     @Override
  17.     public void dress() {
  18.         System.out.println("穿短袖。");
  19.     }
  20. }
复制代码
2.4 Autumn 类

  1. public class Autumn implements Season { // 秋季
  2.         // 单例模式
  3.     private static final Autumn autumn = new Autumn();
  4.     private Autumn() {}
  5.     public static Autumn getInstance() {
  6.         return autumn;
  7.     }
  8.     @Override
  9.     public String getName() {
  10.         return "秋季";
  11.     }
  12.     @Override
  13.     public void festivals() {
  14.         System.out.println("中秋节、重阳节。");
  15.     }
  16.     @Override
  17.     public void dress() {
  18.         System.out.println("从 短袖 向 棉袄 过渡。");
  19.     }
  20. }
复制代码
2.5 Winter 类

  1. public class Winter implements Season { // 冬季
  2.         // 单例模式
  3.     private static final Winter INSTANCE = new Winter();
  4.     private Winter() {}
  5.     public static Winter getInstance() {
  6.         return INSTANCE;
  7.     }
  8.     @Override
  9.     public String getName() {
  10.         return "冬季";
  11.     }
  12.     @Override
  13.     public void festivals() {
  14.         System.out.println("冬至、腊八节、除夕。");
  15.     }
  16.     @Override
  17.     public void dress() {
  18.         System.out.println("穿棉袄。");
  19.     }
  20. }
复制代码
2.6 Person 类

  1. public class Person { // 人
  2.     private int month; // 月份
  3.     private Season season; // 月份对应的季节
  4.     public void setMonth(int month) { // 设置月份和对应的季节
  5.         if (month < 1 || month > 12) {
  6.             throw new IllegalArgumentException("请输入正确的月份");
  7.         }
  8.         this.month = month;
  9.         if (month >= 2 && month < 5) {
  10.             season = Spring.getInstance();
  11.         } else if (month >= 5 && month < 8) {
  12.             season = Summer.getInstance();
  13.         } else if (month >= 8 && month < 11) {
  14.             season = Autumn.getInstance();
  15.         } else {
  16.             season = Winter.getInstance();
  17.         }
  18.     }
  19.     public void showMonth() { // 显示月份和季节的情况
  20.         System.out.println("=================="
  21.                 + month + "月是" + season.getName()
  22.                 + "==================");
  23.     }
  24.     public void showNotAttendClass() { // 显示不上课的情况,除了周末和寒暑假之外
  25.         System.out.print("不上课的日子有:");
  26.         season.festivals();
  27.     }
  28.     public void showDress() { // 显示上衣的穿着情况
  29.         System.out.print("上衣的穿着情况:");
  30.         season.dress();
  31.     }
  32. }
复制代码
2.7 Client 类

  1. public class Client { // 客户端,测试了 人在每个月 不上课的情况 和 上衣的穿着情况
  2.     public static void main(String[] args) {
  3.         Person person = new Person();
  4.         // 每隔 1 秒增加 1 个月
  5.         for (int month = 1; month <= 12; month++) {
  6.             person.setMonth(month);
  7.             person.showMonth();
  8.             person.showNotAttendClass();
  9.             person.showDress();
  10.             try {
  11.                 Thread.sleep(1000);
  12.             } catch (InterruptedException e) {
  13.                 throw new RuntimeException(e);
  14.             }
  15.         }
  16.     }
  17. }
复制代码
2.8 Client 类的运行结果

  1. ==================1月是冬季==================
  2. 不上课的日子有:冬至、腊八节、除夕。
  3. 上衣的穿着情况:穿棉袄。
  4. ==================2月是春季==================
  5. 不上课的日子有:春节、元宵节、清明节。
  6. 上衣的穿着情况:从 棉袄 向 短袖 过渡。
  7. ==================3月是春季==================
  8. 不上课的日子有:春节、元宵节、清明节。
  9. 上衣的穿着情况:从 棉袄 向 短袖 过渡。
  10. ==================4月是春季==================
  11. 不上课的日子有:春节、元宵节、清明节。
  12. 上衣的穿着情况:从 棉袄 向 短袖 过渡。
  13. ==================5月是夏季==================
  14. 不上课的日子有:端午节、劳动节。
  15. 上衣的穿着情况:穿短袖。
  16. ==================6月是夏季==================
  17. 不上课的日子有:端午节、劳动节。
  18. 上衣的穿着情况:穿短袖。
  19. ==================7月是夏季==================
  20. 不上课的日子有:端午节、劳动节。
  21. 上衣的穿着情况:穿短袖。
  22. ==================8月是秋季==================
  23. 不上课的日子有:中秋节、重阳节。
  24. 上衣的穿着情况:从 短袖 向 棉袄 过渡。
  25. ==================9月是秋季==================
  26. 不上课的日子有:中秋节、重阳节。
  27. 上衣的穿着情况:从 短袖 向 棉袄 过渡。
  28. ==================10月是秋季==================
  29. 不上课的日子有:中秋节、重阳节。
  30. 上衣的穿着情况:从 短袖 向 棉袄 过渡。
  31. ==================11月是冬季==================
  32. 不上课的日子有:冬至、腊八节、除夕。
  33. 上衣的穿着情况:穿棉袄。
  34. ==================12月是冬季==================
  35. 不上课的日子有:冬至、腊八节、除夕。
  36. 上衣的穿着情况:穿棉袄。
复制代码
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 总结

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

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

天空闲话

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表