`
xyheqhd888
  • 浏览: 409247 次
  • 性别: Icon_minigender_1
  • 来自: 秦皇岛
社区版块
存档分类
最新评论

State(状态)模式

阅读更多

对象的状态是其属性当前值的一个组合.可以通过调用set-方法或者给对象字段分配值等方式来改变对象的改变。当我们调用对象的某个方法的时候,这个对象的状态也可能发生改变。对象通常在执行其方法时更改自身状态。

 

   在某些情况下,我们使用单词状态(state)来代表某对象的单个可变化属性。比如,我们也许说某机器的状态表述成开机(up)或者关机(down)。在这种情况下,对象状态中可变化部分也许是其行为最重要的方面。最后,依赖于对象状态的处理逻辑也许存在于很多其它类的方法中。其中可能有很多处理逻辑是相近或者类似的,这样会带来沉重的维护负担。

 

   解决这种状态相关处理逻辑分散状况的一种方式是引入新的类组,每个类表示一个不同的状态。然后,把状态相关的处理逻辑放在这些类

 

   State模式的意图在于将与状态有关的处理逻辑分散到代表对象状态的各个类中

 

1.状态建模

   如果一个对象的状态非常重要,那么当我们对这样的一个对象进行建模时,通常会利用一个变量来跟踪对象的行为。该变量可能会出现在复杂的、层叠嵌套的if语句块中。利用这个if语句块,我们可以对对象收到 的事件进行判断处理。使用这种方法来模拟状态存在的问题之一是if语句块会变得非常复杂;该方法存在的另一个问题是当我们对状态模型做调整时,通常不得不对多个方法中的if语句进行调整。而State模式则通过使用一个分布的操作作为解决此类问题提供了一种更为简洁的方法。借助于State模式的,可以把状态模拟为对象,在独立的类中封装其与状态相关的处理逻辑。为了解State模式的工作方式,应该先看看不用State模式情况下系统对状态的建模方式。接下来,我们会重构这部分代码,研究State模式是否能够改进代码设计。

 

  下面我们讨论Oozinoz公司对传送带的入口状态进行建模的软件。传送带设备是一个大型的,智能化的传送带。它可以通过入口接受原料,并能够根据原料材料箱上的条形码来分类存放原料。传送带的入口通过一个按钮来进行控制。当传送带的入口处于关闭状态时,按一下这个按钮,就可以将入口打开。如果在入口还没有完全打开之前,我们又按了一下这个按钮,这个时候入口将开始关闭。如果这个入口处于完全开户状态,那么在2秒钟后,它将会因为超时而自动开始关闭。我们可以在这个入口开启的时候,再按一下它的按钮,从而可以避免入口在两秒超时后自动关闭。图1显示了该传送带入口的状态及其变迁过程。


传送带入口提供一个按钮来改变入口的状态,用户可以通过该按钮控制传送带入口的状态

 

 突破题:假如我们打开了传送带的入口,并在入口处放置了一个材料箱。请问有什么方法可以不用等到超时就让入口关闭?

答:正如状态机所示,当入口打开的时候,我们单击按钮,这个入口将进入StayOpen状态;如果我们再次单击按钮,这个入口将会关闭。

 

  我们可以提供一个Door对象,随着传送带的状态变化,传送带软件能修改该对象。如图2:


Door类对传送带入口进行建模,这个类依赖于传送带设备发出的状态改变事件

 

Door类是Observable的一个子类,借助于这个类,客户端(比如GUI界面)可以查看入口的状态。这个类定义了一个入口所有可能到达的状态,具体代码如下:

package com.oozinoz.carousel;
import java.util.Observable;

public class Door extends Observable
{
     public final int CLOSED = -1;
     public final int OPENING = -2;
     public final int OPEN = -3;
     public final int CLOSING = -4;
     public final int STAYOPEN = -5;

     private int state = CLOSED;

     //......
}

(如果使用Java 5,你也许会选择使用枚举类型。)显而易见,入口状态的文本描述依赖于入口的状态:

 

public String status()
{
     switch(state){
         case OPENING:
               return "Opening";
         case OPEN;
               return "Open";
         case CLOSING:
               return "Closing";
         case STAYOPEN:
               return "StayOpen";
         default:
               return "Closed";
     }
}

当用户单击传送带的"一键式"按钮时,传送带将调用Door对象的touch()方法。Door类中的代码将实现图2所示的状态迁移:

public void touch()
{
     switch(state)  {
          case OPENING:
          case STAYOPEN:
               setState(CLOSING);
               break;
          case CLOSING:
          case CLOSED:
               setState(OPENING);
               break;
          case OPEN:
               setState(STAYOPEN);
               break;
          default:
               throw new Error("can't happen");
     }
}

  

 Door类中的setState()方法将入口的状态变化通知给观察者对象:

private void setState(int state)
{
     this.state = state;
     setChanged();
     notifyObservers();
}

 

突破题:请实现Door类中的complete()方法和timeout()方法。

答: 

public void complete()
{
    if(state == OPENING)
       setState(OPEN);
    else if(state == CLOSING)
       setState(Closed);
}

public void timeout()
{
     setState(CLOSING);
}

 
2.重构为State模式

   由于state变量在整个Door类中都被使用,因而Door类中的代码显得有些复杂。另外,我们很难将状态迁移方法,特别是touch()方法,与图1所示的状态机进行比较。这个时候,state模式可以帮助我们简化上面的代码。为了在本例中应用state模式,我们需要将入口的每个状态定义为一个单独的类。如图3:


该图根据传送带入口的状态机将该入口的状态映射成不同的类

 

 

 

 

经过此图所示的重构,为入口的每个状态都创建一个特殊类。上述每个类都包含单击每种入口状态时对应的状态变化逻辑。比如,文件DoorClosed.java包含如下代码:

 

 

 

DoorClosed类的touch()方法会把入口的新状态通知每个Door2对象。DoorClosed构造器需要参数Door2对象。此处设计要求每个状态对象保持对Door2对象的引用,以便于状态对象可以把状态迁移通知入口。这种设计方法要求状态对象引用某个特定的Door对象,这就使得状态对象只能应用于单个入口。接下来的部分将讨论如何修改这种设计,以保证单个状态集可以满足任意数量入口的需求。当前设计要求创建Door2对象的同时要创建属于该入口的状态集。

DoorState类是一个抽象类,它要求其每个具体子类必须实现touch()方法。状态机中的每个状态都是通过touch()方法完成变迁; 从这点讲,DoorState类层次结构与状态机是一致的。DoorState类将其他会导致状态迁移的方法都移出了这个类,因而DoorState的子类可以重写这些方法,或是直接忽略其中无关的方法,具体代码如下:

 

package com.oozinoz.carousel;

public abstract class DoorState
{

   protected Door2 door;

   public abstract void touch();

   public void complete();
  
   public void timeout() {}

   public String status(){
      String s= getClass().getName();
      return s.substring(s.lastIndexOf('.')+1);
   }

   public DoorState(Door2 door){
      this.door = door;
   }
}

 

注意:status()方法对所有状态都起作用,并且比重构前的代码要简单得多了。

在新设计中,我们并没有改变Door2对象的角色,仍然使用Door2对象从传送带接收状态变化事件。不过现在Door2对象只是简单地将这些状态变化信息转发给当前的state对象:

package com.oozinoz.carousel;
import java.util.Observable;

public class Door2 extends Observable{
    // variable and constructor ...

    public void touch(){
        state.touch();
    }

    public void complete(){
        state.complete();
    }

    public void timeout(){
        state.timeout();  
   }
  
   public String status(){
         return state.status();
    }

   protected void setState(DoorState state){
         this.state = state;
         setChanged();
         notifyObservers();
   } 
}

 

  touch(),complete(),status()和timeout()方法体现了多态性在这种设计中的应用。每个方法都代表了一种状态迁移方式。在每种情况下,操作是固定的,但是接收者类(state类)可能不同。多态性的原则就是实际执行哪个方法不仅取决于操作的方法签名,还取决于操作的接收者类。当我们调用touch()方法时会发生什么呢?答案是执行结果依赖于传送带入口的状态。现在的代码仍然可以高效地执行状态迁移,但是由于使用了多态性,故而代码比以前简单多了。

 

  DoorState的子类使用了Door2类中的setState()方法。这些子类与图1中的状态机很好的对应起来了。例如,DoorOpen代码中的touch()方法和timeout()方法分别对状态机中的Open状态的两种变迁做了处理:

 

突破题:请实现DoorClosing.java。

答:Closing代码中应有完成后到达Closed状态和单击按钮时到达Opening状态两种迁移方式,代码如下:

package com.oozinoz.carousel;
public class DoorClosing extends DoorState{
    public DoorClosing(Door2 door){
      super(door); 
   }

   public void complete(){
      door.setState(door.CLOSED);
   }

   public void touch(){
      door.setState(door.OPENING);
   }
}

 新设计使得代码更加简单,不过我们可能仍然感觉有点不满意,因为Door类使用的“常量”其实是局部变量。

 

3.使状态成为常量

  State模式把具体状态的处理逻辑迁移到表示对象状态的类中。但是对于状态对象和中心对象之间的通信和依赖关系,State模式并没有说明如何管理。在以前的设计方案中,每个状态类的构造器都接收一个Door对象。状态对象保持该对象,并用其来修改入口的状态。这并不是说构造器一定是个非常糟糕的设计,但是实例化Door对象确实会导致DoorState对象集初始化方面的问题。你也许更愿意创建单个静态的DoorState对象集合,并要求Door对象管理来自于状态变更的所有变化。

  使得状态对象变成常量的一个方法是让状态类简单地标识下一个状态,让Door类来更新其state变量。在这种设计中,Door类的touch()方法负责更新state变量,如下代码所示:

public void touch(){
    state = state.touch();
}

请注意,Door类的touch()方法的返回类型是void。DoorState类的子类也会实现touch(),但是这些实现将返回DoorState值。比如,DoorOpen类的touch()方法代码如下所示:

public DoorState touch(){
   return DoorState.STAYOPEN;
}

   在这种设计思路中,DoorState对象不保留对Door对象的引用,所以该应用程序只需要每个DoorState对象的单个实例。

   使得DoorState对象变成常量的另外一个方法是在状态迁移时传递给中心的Door对象。你可以给complete(),timeout()和touch()等状态变更方法添加一个参数:Door对象。在无需引用的情况下,这些方法接收中心的Door对象,并更新其状态。

 

突破题:请完成图4所示的类图,把DoorState对象变成常量,并在状态迁移时传递Door对象。

答:图4最终结果如下所示:


本设计把DoorState对象变成常量。DoorState状态迁移方法会更新Door对象的状态,

Door对象作为这些方法的接收参数

 

      当应用State模式时,你可以随意决定如何处理状态变更的通信情况。这些状态类保留对中心对象的引用,该中心对象的状态已经建模。同样,你可以在状态迁移过程中传递这个对象。也可把状态子类作为纯粹信息接收者,它们可以决定下一个状态,但是不能更新中心对象。至于选择何种方法则取决于应用的具体领域或者个人爱好。

 

     如果状态被不同线程使用,请保证状态迁移方法同步,以防止两个纯种同时修改状态时发生冲突。

     State模式的作用是可以将任何特定状态的处理逻辑集中放入单个类中

 

4.小结

  一般来说,一个对象中各个实例变量的值决定了这个对象的当前状态。在某些情况下,对象的大多数属性一旦设置就不会变化;一个属性是动态变化的,并在类的算法逻辑中发挥重要作用。这个属性出庭代表整个对象的状态,甚至是命名的state。

 

   现实生活中有些实体的状态非常关键,诸如事务或者机器,当使用对象对这个实体建模时,经常会出现一个极其重要的状态变量。在这种情况下,依赖对象状态的处理逻辑也许会出现在很多方法中。通过把状态相关行为迁移到状态对象层次,就可以简化代码。这使得每个状态类都包含应用领域中一个状态的行为。这样一来,状态类便可直接对应到状态机的状态。

 

  为处理状态之间的迁移,可以让中心对象保留对状态集的引用。或者,在状态迁移调用中,可以传递状态正在改变的中心对象。也可让状态类成为信息提供者,只提示接下来的状态,而无需更新中心对象。不论怎样管理状态迁移,都可应用State模式将对象的不同状态用一个状态类集合来表示,并将操作分散在这个状态类的集合中,从而简化我们的代码。

  • 大小: 5.1 KB
  • 大小: 2.5 KB
  • 大小: 6.6 KB
  • 大小: 4.7 KB
分享到:
评论

相关推荐

    C#面向对象设计模式纵横谈(22):(行为型模式) State 状态模式

    标题和描述均提到了"C#面向对象设计模式"中的State状态模式,这表明文章的核心内容是围绕State模式在C#编程语言中的应用展开。State模式是一种行为设计模式,旨在允许对象在其内部状态改变时,其行为也能相应地改变...

    haoyGo#weekly-1#186.精读《设计模式 - State 状态模式》1

    重点在 “内部状态” 的理解,也就是状态改变是由对象内部触发的,而不是外部,所以 外部根本无需关心对象是否用了状态模式,拿数据库连接器的例子来说,不管这个类是用

    详解state状态模式及在C++设计模式编程中的使用实例

    主要介绍了state状态模式及在C++设计模式编程中的使用实例,在设计模式中策略用来处理算法变化,而状态则是透明地处理状态变化,需要的朋友可以参考下

    设计模式C++学习之状态模式(State)

    状态模式是一种行为设计模式,它允许对象在内部状态改变时改变其行为,对象看起来似乎修改了它的类。这种模式常用于处理对象的状态变化,并且使代码结构清晰,易于维护。 在C++中,状态模式通常包含以下几个关键...

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

    状态模式是一种行为设计模式,它使对象能够在内部状态改变时改变其行为,看起来好像改变了它的类。这种模式常用于处理对象在不同状态下表现各异的情况,避免了复杂的条件语句,提高了代码的可读性和可维护性。 在...

    状态模式 State Pattern

    ### 状态模式(State Pattern) #### 概念与定义 状态模式是一种行为设计模式,它允许对象在其内部状态改变时改变其行为,使对象看起来像是修改了它的类。该模式通过引入一个代表各种状态的类以及一个行为随着这些...

    设计模式之状态模式State

    状态模式通常包含三个主要角色:Context(上下文)、State(抽象状态)和ConcreteState(具体状态)。 上下文是拥有状态的对象,它定义了与该状态相关的接口,并负责在适当的时候将请求委托给相应的状态对象处理。...

    实例讲解C++设计模式编程中State状态模式的运用场景

    在给出的实例中,`Context`类是状态模式的核心,它定义了所有可能的状态行为,并持有当前状态对象的引用。`Context`类有两个主要的方法:`Handle()`和`OperationForStateA()`以及`OperationForStateB()`。`Handle()`...

    设计模式之状态模式(State Pattern)

    状态模式是一种行为设计模式,它使你能在运行时改变对象的行为。在状态模式中,一个对象的状态变化会导致其行为的变化,这种变化不是通过改变对象的类来实现的,而是通过改变对象的状态。这个模式的核心是封装可能...

    iOS App的设计模式开发中对State状态模式的运用

    状态模式是一种设计模式,它允许对象在内部状态改变时改变其行为,使对象看起来似乎修改了它的类。在iOS App的开发中,特别是在Objective-C环境下,状态模式可以帮助开发者更好地管理对象的状态并根据状态执行相应的...

    设计模式State模式源码

    State模式在实际使用中比较多,适合"状态的切换".因为我们经常会使用If elseif else 进行状态切换, 如果针对状态的这样判断切换反复出现,我们就要联想到是否可以采取State模式了. 不只是根据状态,也有根据属性.如果...

    《设计模式》实战---状态模式(State Pattern)

    在《设计模式》实战---状态模式(State Pattern)这篇文章中,作者可能详细探讨了以下关键点: 1. **模式定义**:状态模式允许对象在内部状态改变时改变其行为,对象看起来好像修改了它的类。这通过将每个状态封装...

    c++状态模式

    状态模式是一种行为设计模式,它允许对象在内部状态改变时改变其行为,对象看起来似乎修改了它的类。在C++中,状态模式通常通过定义一系列的类来表示各种状态,并用一个上下文类来管理这些状态的切换。下面将详细...

    轻松掌握state设计模式

    3. **复杂度考量**:虽然State模式能够清晰地表示状态转换,但如果状态较少且转换逻辑简单,则可能不需要使用State模式,因为引入额外的类可能会增加不必要的复杂度。 #### 四、State模式的实例解析 本节将以自动...

    JackChan1999#Java-Degisn-Patterns#状态模式-State Pattern1

    状态模式-State Pattern状态模式-State Pattern【学习难度:,使用频率:】状态模式-State Pattern处理对象的多种状态及其相互

    Android的状态机模式StateMachine与State

    状态机模式(StateMachine)在Android开发中是一种常用的设计模式,特别是在处理复杂的系统行为时,它可以帮助我们更好地组织代码,使其更具有可读性和可维护性。Android的状态机模式主要涉及两个核心概念:State...

    设计模式 - 状态模式(C++实例)

    在C++中实现状态模式,我们通常会定义一个抽象状态类(State),它声明了所有可能的状态行为。然后,创建一系列具体状态类(ConcreteState)来实现这些行为。每个具体状态类代表一种特定的状态,并且在内部维护当前...

    C++设计模式课件18_State_状态模式.pdf

    ### C++设计模式之状态模式解析 #### 一、引言 在软件开发过程中,对象的行为往往会根据其内部状态的变化而变化。为了更好地管理和控制这些行为的变化,设计模式中的“状态模式”应运而生。状态模式允许一个对象在...

    java 设计模式之状态模式

    状态模式是一种行为设计模式,它允许对象在内部状态改变时改变其行为,看起来好像对象改变了它的类。在Java中,状态模式通常通过定义不同的状态类和一个上下文类来实现,其中上下文类持有状态对象并调用其方法来响应...

Global site tag (gtag.js) - Google Analytics