• 状态(State)模式


     状态模式中的行为是由状态来决定的,不同的状态下有不同的行为。如人都有高兴和伤心的时候,不同的情绪有不同的行为,当然外界也会影响其情绪变化。对这种有状态的对象编程,传统的解决方案是:将这些所有可能发生的情况全都考虑到,然后使用 if-else 或 switch-case 语句来做状态判断,再进行不同情况的处理。但是显然这种做法对复杂的状态判断存在天然弊端,条件判断语句会过于臃肿,可读性差,且不具备扩展性,维护难度也大。且增加新的状态时要添加新的 if-else 语句,这违背了“开闭原则”,不利于程序的扩展。

    状态模式和策略模式两者的结构几乎完全一样,主要有以下几点区别

    1、状态模式的行为是平行的、不可替换的;策略模式的行为是彼此独立、可相互替换的。

    2、状态模式重点在各状态之间的切换,从而做不同的事情;而策略模式更侧重于根据具体情况选择策略,做的都是同一件事。例如,某支付平台,有支付宝、微信支付、银联支付、云闪付,虽然策略不同,但最终做的事情都是支付,也就是说他们之间是可替换的。反观状态模式,各个状态下做的是不同的事,不能互相替换。

    3、状态模式封装对象的状态,因与对象密切相关,很难被重用;而策略模式封装算法或策略较易重用。

    4、状态模式将各个状态所对应的操作分离开来,即对于不同的状态,由不同的子类实现具体操作,不同状态的切换由子类实现,当发现传入参数不是自己这个状态所对应的参数,则自己给环境类(Context类)切换状态。状态模式允许对象在内部状态改变时改变它的行为,对象看起来好像修改了它的类。而策略模式是直接依赖注入到Context类的参数进行策略选择,不存在切换状态的操作。

    定义:

    对有状态的对象,把复杂的“判断逻辑”提取到不同的状态对象中,允许状态对象在其内部状态发生改变时改变其行为。

    优点:

    1、封装了转换规则。结构清晰,状态模式将与特定状态相关的行为局部化到一个状态中,并且将不同状态的行为分割开来,满足“单一职责原则”。

    2、将不同的状态引入独立的对象中会使得状态转换变得更加明确,且减少对象间的相互依赖。

    3、状态类职责明确,有利于程序的扩展。通过定义新的子类很容易地增加新的状态和转换。

    缺点:

    1、状态模式的使用必然会增加系统的类与对象的个数。

    2、状态模式的结构与实现都较为复杂,如果使用不当会导致程序结构和代码的混乱。

    3、状态模式对开闭原则的支持并不太好,对于可以切换状态的状态模式,增加新的状态类需要修改那些负责状态转换的源码,否则无法切换到新增状态,而且修改某个状态类的行为也需要修改对应类的源码。

    使用场景: 

    1、对象行为随状态改变而改变的场景。

    2、代码中包含大量与对象状态有关的条件语句。

    模式的结构:状态模式包含以下主要角色。

    1、环境类(Context)角色:也称为上下文,它定义了客户端需要的接口,内部维护一个当前状态,并负责具体状态的切换。

    2、抽象状态(State)角色:定义一个接口,用以封装环境对象中的特定状态所对应的行为,可以有一个或多个行为。

    3、具体状态(Concrete State)角色:实现抽象状态所对应的行为,并且在需要的情况下进行状态切换。

    示例:电视及状态简单分为开机、关机两种状态。开机状态下可以通过遥控器进行频道切换、调整音量等,此时重复按开机键无效;关机状态下,频道切换啊、调整音量、关机都是无效的,只有按开机键生效。下面用状态模式进行实现。

    先定义电视操作的接口

    1. /**
    2. * 电视状态接口,定义了电视操作的函数
    3. */
    4. public interface TvState {
    5. void nextChannel(); //下一个频道
    6. void preChannel(); //上一个频道
    7. void turnUp(); //调高音量
    8. void turnDown(); //调低音量
    9. }

    定义开机状态和关机状态具体类中功能的实现 

    1. /**
    2. * 开机状态
    3. */
    4. public class PowerOnState implements TvState{
    5. @Override
    6. public void nextChannel() {
    7. System.out.println("下一个频道");
    8. }
    9. @Override
    10. public void preChannel() {
    11. System.out.println("上一个频道");
    12. }
    13. @Override
    14. public void turnUp() {
    15. System.out.println("调高音量");
    16. }
    17. @Override
    18. public void turnDown() {
    19. System.out.println("调低音量");
    20. }
    21. }
    22. /**
    23. * 关机状态
    24. */
    25. public class PowerOffState implements TvState{
    26. @Override
    27. public void nextChannel() {
    28. }
    29. @Override
    30. public void preChannel() {
    31. }
    32. @Override
    33. public void turnUp() {
    34. }
    35. @Override
    36. public void turnDown() {
    37. }
    38. }

    定义电源操作接口 

    1. /**
    2. * 电源操作接口
    3. */
    4. public interface PowerControl {
    5. void powerOn();
    6. void powerOff();
    7. }

    遥控器功能实现

    1. /**
    2. * 遥控器
    3. */
    4. public class TvControl implements PowerControl{
    5. TvState mState;
    6. public void setState(TvState state){
    7. mState = state;
    8. }
    9. //改变为开机状态
    10. @Override
    11. public void powerOn() {
    12. //改变为开机状态,把状态设置为开机状态
    13. setState(new PowerOnState());
    14. System.out.println("开机啦");
    15. }
    16. //改变为关机状态
    17. @Override
    18. public void powerOff() {
    19. //改变为关机状态,把状态设置为开机状态
    20. setState(new PowerOffState());
    21. System.out.println("关机啦");
    22. }
    23. public void nextChannel(){
    24. mState.nextChannel();
    25. }
    26. public void preChannel(){
    27. mState.preChannel();
    28. }
    29. public void turnUp(){
    30. mState.turnUp();
    31. }
    32. public void turnDown(){
    33. mState.turnDown();
    34. }
    35. }

    客户端测试类

    1. /**
    2. * 客户端测试类
    3. */
    4. public class StateTest {
    5. public static void main(String[] args){
    6. test();
    7. }
    8. public static void test(){
    9. //电视
    10. TvControl tvControl = new TvControl();
    11. //改变状态,看看是否影响行为
    12. tvControl.powerOn();
    13. //下一个频道
    14. tvControl.nextChannel();
    15. //调大音量
    16. tvControl.turnUp();
    17. //设置关机状态
    18. tvControl.powerOff();
    19. //调高音量,此时不会生效
    20. tvControl.turnUp();
    21. }
    22. }

    输出:

    开机啦
    下一个频道
    调高音量
    关机啦
    

    环境类实际上就是拥有状态的对象,环境类有时候可以充当状态管理器(State Manager)的角色,可以在环境类中对状态进行切换操作。抽象状态类可以是抽象类,也可以是接口。

  • 相关阅读:
    双倍数据速率I/O (ALTDDIO_IN、ALTDDIO_OUT)使用方法
    微擎模块 崛企图文 1.1.7 后台模块+前端小程序,无限多开商用版
    聊聊在不确定环境下的个人成长
    Go语言的流程控制
    网页制作课作业基于HTML+CSS+JavaScript+jquery仿慕课网教学培训网站设计实例 企业网站制作
    使用ES检索PDF或Word等格式文件方案
    MyBatis拦截器 Interceptor 实现多数据源切换
    Vue解决Echarts组件使用ID不能复用问题
    VisualODX——ODX数据自动转换工具
    R语言ggplot2可视化:使用patchwork包将多个ggplot2可视化结果组合起来、两个可视化图像纵向组合之后再和另外一个可视化结果横向组合
  • 原文地址:https://blog.csdn.net/u013773608/article/details/125880856