设计模式之状态模式(六)

作者: 3d0829501918 | 来源:发表于2019-03-09 21:55 被阅读12次

      我们在购物网站进行购物时,订单会产生几种状况:已下单、已付款、送货中、确定收货等状态。所以系统会判断该订单的状态,不管是哪种状态都应给出对应的操作,这就是状态。


    一、什么是状态模式

      在软件开发过程中,应用程序可能会根据不同的情况作出不同的处理。最直接的解决方案是将这些所有可能发生的情况全都考虑到。然后使用if... else语句来做状态判断来进行不同情况的处理。但是对复杂状态的判断就显得“力不从心了”。随着增加新的状态或者修改一个状体(if else(或switch case)语句的增多或者修改)可能会引起很大的修改,而程序的可读性,扩展性也会变得很弱。维护也会很麻烦。这时就要考虑只修改自身状态的模式。


    二、状态模式的类图


    三、状态模式角色组成

    \color{rgb(255, 20, 147)}{环境角色 (Context)}

    客户程序需要的接口,并且维护一个具体状态的实例,这个实例决定当前状态。

    \color{rgb(255, 20, 147)}{状态角色 (State)}

    定义一个接口以封装与使用环境角色的一个特定状态的相关行为

    \color{rgb(255, 20, 147)}{具体状态角色 (ConcreteState)}

    实现状态角色定义的接口,结构十分简单与策略模式相似。


    四、状态模式的特征

    \color{rgb(255, 20, 147)}{抽象状态角色(State)}

    定义一个接口用以封装对象的一个特定状态所对应的行为。

    \color{rgb(255, 20, 147)}{具体状态(Concrete State)}

    一个具体的状态的实现类实现了Context对象的一个状态所对应的行为。

    \color{rgb(255, 20, 147)}{环境角色(Context}

    定义客户端所感兴趣的接口,并且保留一个具体状态类的实例,这个具体状态类的实例给出环境对象的现有实例。


    五、状态模式的优缺点

    \color{rgb(255, 20, 147)}{优点}

      每个状态都是一个子类,只要增加状态就要增加子类,修改状态,只修改一个子类即可。
      结构清晰,避免了过多的switch...case或者if...else语句的使用,避免了程序的复杂性,提高可维护性。
      State对象可被共享 如果State对象没有实例变量—即它们表示的状态完全以它们的类型来编码—那么各Context对象可以共享一个State对象。当状态以这种方式被共享时, 它们必然是没有内部状态, 只有行为的轻量级对象。

    \color{rgb(255, 20, 147)}{缺点}

      状态模式的使用必然会增加系统类和对象的个数。
      状态模式的结构与实现都较为复杂,如果使用不当将导致程序结构和代码的混乱。


    六、状态模式和策略模式区别

      在状态模式中,状态的变迁是由对象的内部条件决定,外界只需关心其接口,不必关心其状态对象的创建和转换。
      而策略模式里,采取何种策略由外部条件(C)决定。Strategy模式与State模式的结构形式完全一样。但它们的应用场景(目的)却不一样,State模式重在强调对象的内部状态的变化改变对象的行为,Strategy模式重在外部对策略的选择,策略的选择由外部条件决定,也就是说算法动态的切换。但由它们的结构是如此的相似。我们可以认为状态模式是完全封装且自修改的策略模式。所以说策略和状态模式是孪生兄弟。


    七、状态模式的适用性

      一个对象的行为取决于它的状态, 并且它必须在运行时刻根据状态改变它的行为。
      代码中包含大量与对象状态有关的条件语句:一个操作中含有庞大的多分支的条件(if else(或switch case)语句,且这些分支依赖于该对象的状态。这个状态通常用一个或多个枚举常量表示。通常 , 有多个操作包含这一相同的条件结构。 State模式将每一个条件分支放入一个独立的类中。这使得你可以根据对象自身的情况将对象的状态作为一个对象,这一对象可以不依赖于其他对象而独立变化。


    八、JDK中用到状态模式

    java.util.Iterator
    迭代器模式的角色组成
      迭代器角色(Iterator):迭代器角色负责定义访问和遍历元素的接口。
      具体迭代器角色(Concrete Iterator):具体迭代器角色要实现迭代器接口,并要记录遍历中的当前位置。
      容器角色(Container):容器角色负责提供创建具体迭代器角色的接口。
      具体容器角色(Concrete Container):具体容器角色实现创建具体迭代器角色的接口—这个具体迭代器角色与该容器的结构相关。
      javax.faces.lifecycle.LifeCycle#execute()
      FacesServlet由此控制,行为取决于JSF生命周期的当前阶段(状态)


    九、代码示例

    public class Order {
    
    
    public static  void  main(String [] args) {
        // 关于订单状态的事情,大家有没有这样判断过
        String stateName = getStateName(1);
        System.out.println(stateName);
    }
    
    /**
     * @method  getStateName
     * @description 获取订单状态的方法
     *              订单状态1、已下单2、已付款3、已发货4、送货中5、确认收货
     * @date: 2018/12/19 22:04
     * @author: Ni Shichao
     * @param status 状态
     * @return
     */
    public  static  String getStateName (int status) {
        String stateName = "" ;
        if (status == 1) {
            stateName = "已下单";
        } else if (status == 2) {
            stateName = "已付款";
        } else if (status == 3) {
            stateName = "已发货";
        } else if (status == 4) {
            stateName = "送货中";
        } else if (status == 5) {
            stateName = "确认收货";
        }
        return stateName;
    }
    }
    

     下面是使用状态模式的小例子:
    \color{rgb(255, 20, 147)}{State接口}

    public interface State {
           void handle();
    }
    

    \color{rgb(255, 20, 147)}{Context类}

    public class Context {
    
          private State state;
    
          public Context() {}
    
          public Context(State state) {
              this.state = state;
          }  
    
          public void setState(State state) {
                  System.out.println("订单信息已更新!");
                  this.state = state;
                  this.state.handle();
          }
    
    }
    

    \color{rgb(255, 20, 147)}{Concrete State类}

        public class Booked implements  State {
    
           @Override
           public void handle() {
                System.out.println("您已下单!");
           }
        }
    

    \color{rgb(255, 20, 147)}{Client测试类}

     public class Client {
       public static  void  main(String [] args) {
             Context context = new Context();
             context.setState(new Booked());
             context.setState(new Payed());
             context.setState(new Sended());
             context.setState(new InWay());
             context.setState(new Recieved());
       }
    }
    

    \color{rgb(255, 20, 147)}{运行结果:}

    订单信息已更新!
    您已下单!
    订单信息已更新!
    您已付款!
    订单信息已更新!
    已发货!
    订单信息已更新!
    送货中。。。
    订单信息已更新!
    已确认收货!


    十、总结

    状态模式的主要优点在于封装了转换规则,并枚举可能的状态,它将所有与某个状态有关的行为放到一个类中,并且可以方便地增加新的状态,只需要改变对象状态即可改变对象的行为,还可以让多个环境对象共享一个状态对象,从而减少系统中对象的个数;其缺点在于使用状态模式会增加系统类和对象的个数,且状态模式的结构与实现都较为复杂,如果使用不当将导致程序结构和代码的混乱,对于可以切换状态的状态模式不满足“开闭原则”的要求。

    相关文章

      网友评论

        本文标题:设计模式之状态模式(六)

        本文链接:https://www.haomeiwen.com/subject/ujjdpqtx.html