前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >如何掌握状态模式,提升项目的可维护性和可扩展性?

如何掌握状态模式,提升项目的可维护性和可扩展性?

作者头像
程序视点
发布2023-09-13 12:35:15
1750
发布2023-09-13 12:35:15
举报
文章被收录于专栏:程序小小事程序小小事
今天和大家聊一聊状态模式(State Pattern)

个人觉得这个模式有点难理解,大家做好心里准备!

简介

还是把这张概总图放这里。

状态模式,也是一种行为设计模式。

重点:对象的行为,由其内部的状态决定。内部状态变化,对应的行为改变。

有的小伙伴分不清状态模式命令模式。觉得,一个命令对应了一个操作一个状态对一个行为,在逻辑上一样的。

其实不一样。区别在于:对象的内部状态是变化的。状态变化后,对应的行为也会变化。但这个行为被调用后,对象的内部状态进入下一个状态。状态与下一个状态之间是有关联的。

接下来要进一步理解内部状态的意思。也就是说这个内部状态的变化是由对象内部触发的。在外部看来,根本无需关系对象是否使用了状态模式,直接调用行为就好!

结构讲解和示例

前面说到状态会从一个状态变为另一个,并且这个状态是对象内部的一个状态。也就是说,对象内部某时某刻只维护着一种状态。这个状态怎么表示呢?

当然是定义各个状态的抽象接口类,然后各个状态都是实现这个接口类。这样对象维护的状态,只要是这个抽象接口类的类型就可以了。

代码语言:javascript
复制
public interface State {
  // TO DO
}

为什么这里要设计一个顶层状态接口呢?主要是因为,要把状态的行为从状态所在的对象上抽离到状态类上。为什么?

如果我们状态所对应的行为在保留在对象上。那么对象在执行行为的时候,就需要判断当前的状态是什么,接着在执行对应的操作。如果这样做,大家能想象到一堆if-else的语句吗?这实在是太糟糕了~

于是乎!状态的行为需要抽离到这个状态类上。因此,我上面才说用接口。其实你用抽象类也可以。主要是把行为给弄到状态类上。

ps:行为抽离到状态类上,不仅是因为对象中,if-else判断状态违背了开闭原则,而且抽离到状态后,对扩展更好,添加新状态和新行为更方便。

我们接着完善状态类。

代码语言:javascript
复制
public interface State {
  void doAction(ContextObject contextObject);
}

这里的ContextObject contextObject就是对象。把它传递过来,主要是方便调用对象的行为。

我们接着把这个对象的类定义出来。

代码语言:javascript
复制
public class ContextObject{
  // 对象中维护的状态
  private State state;
  //设置新状态
  public void setState(State state)
  {
      this.state=state;
  }
  //读取状态
  public State getState()
  {
      return(state);
  }
  //处理当前状态对应的行为
  public void handle()
  {
      state.doAction(this);
  }
}

现在,我们来实现两个具体的状态类。

代码语言:javascript
复制
class ConcreteStateA extends State
{
    public void Handle(ContextObject context)
    {
        System.out.println("当前状态是 A.");
        context.setState(new ConcreteStateB());
    }
}
class ConcreteStateB extends State
{
    public void Handle(ContextObject context)
    {
        System.out.println("当前状态是 B.");
        context.setState(new ConcreteStateA());
    }
}

上面的两个状态:ConcreteStateAConcreteStateB。它们的行为是:打印当前的状态,并把对象ContextObject的状态变为另一个状态。

让我们来测试下:

代码语言:javascript
复制
public class StateTest {
  public static void main(String[] args) {
    ContextObject context = new ContextObject();
    ConcreteStateA initialState = new ConcreteStateA();
    //设置对象的初始状态。
    context.setState(initialState);
    
    //开始调用当前状态的行为。
    context.handle();
    // 执行完上一步,context中的状态变化了。可以接着执行变化后的状态对应的行为。
    context.handle();
    //还可以接着执行执行。
    context.handle();
    context.handle();
  }
}

运行输出:当前状态是 A.当前状态是 B.当前状态是 A.当前状态是 B.

小结

状态模式的结构不是很好理解。尤其是其与策略模式和命令模式有许多相似的地方。这需要大家对比区分出不同。小二哥后面也会单独写一篇文章来讲它们的区别。

今天要记住的就是:

  • 必须要有对象类(ContextObject),和状态类(State),对象类持有状态类的顶层接口。对象类负责保持并切换状态。
  • 状态是要变化的。状态的变化可以在具体的State类中变化,也可以在使用ContextObject的时候进行变化;但就是不能在ContextObject类中变化。

当使用ContextObject类改变状态时,状态类之间互相不认识,他们直接的依赖关系应该由客户端负责。

当使用具体的State类切换时,状态直接就可能互相认识,一个状态执行完就自动切换到了另一个状态去了。

优缺点

优点

  • 隔离变化的状态,增强了扩展性。增加一个状态非常简单;
  • 增强了封装性,每个状态的行为操作都被封装到一个状态类中。

缺点类变多了~~哈哈(放心,你写的内容没有变多哦)

再次强调一下:对象的行为随着状态的变化而不同的情况,请用状态模式。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2023-07-21 08:08,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 程序视点 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 简介
  • 结构讲解和示例
  • 小结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档