java state_在Java中应用State设计模式

在Java中应用State设计模式

作者:未知    文章来源:www.jspcn.net

发布日期:2005年01月19日

在Java中应用State设计模式

作者:俞良松 本文选自:开放系统世界??赛迪网 2002年11月19日

对象的状态由各个属性的当前值构成。当我们调用某个对象的setXXX()方法时,通常表示修改它的XXX属性。另外,对象在执行方法时,也可能修改自己的状态。在某些情形下,例如建立事务或机器模型时,对象的状态可能是决定其行为的关键因素,依赖于状态的代码逻辑可能遍布于类的大量方法。State模式的目标就是简化这类代码,把依赖于状态的逻辑集中到一组类,每一个类代表一种不同的状态,避免if语句嵌套过深或过于复杂,转而依赖于多态性来调用不同的方法。

状态模型

如果对象的状态信息很关键,对象会拥有一些变量来指示如何根据状态做出相应的动作。这些变量大量地散布于复杂的多层嵌套if语句中,来描述对象如何响应可能出现的事件。用这种方式建立对象模型的最大缺点在于if语句可能变得相当复杂一旦要修改对象的状态模型,往往有多个方法的许多if语句需要调整。

以传送带的门为例,考虑其状态变化过程为:传送带的门由单个按钮控制,并且假设初始时处于关闭状态。按一下按钮门开始打开,如果在门完全打开之前再次按下按钮,门开始关闭。一旦门完全打开,它将在2秒延时之后自动开始关闭过程。要禁止门自动关闭,可以在门打开之后按一下按钮。图1描述了传送门的状态变化情况。它是一个UML状态机(State Machine),其中click表示按下按钮的动作。显然,与纯文字描述相比UML状态机图示更加直观易懂。

按照常规的设计思路(不使用State设计模式),在模拟传送带工作过程的软件中,可以使用一个Door1对象代表传送门(如图2所示),状态改变事件由传送带软件发送给Door1对象。

11049337929530752.gif

screen.width-333)this.width=screen.width-333;">

图1 UML状态机

11049337965151323.gif

screen.width-333)this.width=screen.width-333;">

图2 状态改变事件发送给Door1对象

Door1类从Observable派生,这样客户程序(例如一个GUI程序)就能够方便地了解传送门状态。Door1类首先定义传送门可能处于的状态,代码如下:

public class Door1 extends Observable {

public static final int CLOSED = 1;

public static final int OPENING = 2;

public static final int OPEN = 3;

public static final int CLOSING = 4;

public static final int STAYOPEN = 5;

private int state = CLOSED;

//...

}

status()方法返回传送门状态的文字描述,如下所示:

public String status() {

switch (state) {

case OPENING :

return "正在打开";

case OPEN :

//...

default :

return "已关闭";

}

}

当用户点击传送带的按钮时,传送带程序调用Door1对象的click()方法。click()方法模拟图1所示的状态装换过程:

public void click() {

if (state == CLOSED) {

setState(OPENING);

}

else if (state == OPENING || state == STAYOPEN) {

setState(CLOSING);

}

else if (state == OPEN) {

setState(STAYOPEN);

}

else if (state == CLOSING) {

setState(OPENING);

}

}

Door1类的setState()方法向观察者通知传送门状态改变事件,代码如下:

private void setState(int state) {

this.state = state;

setChanged();

notifyObservers();

}

用State模式改造

Door1类的代码比较复杂,整个类到处都用到了状态变量。如果要比较图1的状态机和Door1类的各个状态变换方法,将是非常困难的,click()方法尤其如此。那么,怎样在这个例子中应用State模式呢?首先要把传送门的各种状态分别定义成类,如图3所示。图3能够更好地与图1的状态机对应。更改后的类设计中,Door2包含了状态机的上下文信息。所谓上下文信息,就是描述环境和一系列其它对象相关的信息。就本例而言,State利用一个上下文对象记录了传送门的当前状态是DoorState类的哪一个实例。

11049337968432222.gif

screen.width-333)this.width=screen.width-333;">

图3 传送门各个状态

DoorState类的构造函数要求提供一个Door2对象,DoorState的子类利用该对象传达状态变更信息。在这种设计方案中,DoorState的子类通过一个Door2类型的属性绑定到特定的传送门(Door2)对象,因而要求一个DoorState对象只被一个Door2对象引用。同时Door类要把它的状态信息定义成局部变量,代码如下:

public class Door2 extends Observable {

public final DoorState CLOSED = new DoorClosed(this);

// 按照类似方式定义DoorState类型的

// OPENING、OPEN、CLOSING、STAYOPEN对象(略)

private DoorState state = CLOSED;

// ...

}

DoorState类是一个抽象类,由子类实现其click()方法。在状态机中,每一个状态均有相应的“按下按钮”操作。修改后的设计中每一个描述状态的类也有一个click()方法,两者是一致的。DoorState类处理了其它可能的变换,所以DoorState的子类可以忽略无关的事件,代码如下:

public abstract class DoorState {

protected Door2 door;

public DoorState(Door2 door) {

this.door = door;

}

public abstract void click();

public String status() {

String s = getClass().getName();

return s.substring(s.lastIndexOf(´.´) + 1);

}

public void complete() { }

public void timeout() { }

}

由上可以看到,现在的status()方法要比修改设计方案之前的status()方法简单多了。新status()方法返回的结果与修改前版本的结果略有不同,它的状态信息从类的名称获得。如果要返回修改设计方案之前的信息,只需把这些状态信息分别记录到DoorState的各个子类中,然后在这个status()方法中直接提取即可。

新的设计方案中,传送门对象(Door2)从传送带接收状态改变信息的这一角色仍未改变。但现在Door2对象只需把这些状态改变信息直接传递给当前的状态对象就可以了,代码如下:

public class Door2 extends Observable {

// ... 声明变量

protected void setState(DoorState state) {

this.state = state;

setChanged();

notifyObservers();

}

public void click() {

state.click();

}

// complete()、status()、timeout()都直接

// 调用state的相应方法即可(略)

}

这里的click()、complete()、status()和timeout()方法体现了Java类多态性的应用。所有这些方法都起着判断和选择动作的作用,即是虽然每一个方法的代码是不含if判断逻辑的,但实际运行时被调用的状态对象却不断变化。在调用click()时会发生哪些事情呢?按照多态性规则,答案依赖于当时传送门的状态。修改后的代码有效地担负起了根据状态执行不同动作的任务,但由于利用了多态性,它变得更加简单了。

Door2类中的setState()方法现在由DoorState的子类调用。这些DoorState的子类与图1状态机中的相应实体很相似,例如,状态机中Open状态包含Timeout和Click,DoorOpen类则包含两个对应的方法timeout()和click()。DoorOpen类的代码如下:

public class DoorOpen extends DoorState {

public DoorOpen(Door2 door) {

super(door);

}

public void click() {

door.setState(door.STAYOPEN);

}

public void timeout() {

door.setState(door.CLOSING);

}

}

从上面可以看到,利用State设计模式之后,代码变得更简单了。不过,细心的读者或许已经注意到,Door2类用到的“常量”实际上是变量,这给人一种不规范的感觉。假设现在要把这些状态常量移到_DoorConstant接口,这就需要从DoorState类消除Door2实例变量。修改办法是:重新定义DoorState类中的click()、complete()和timeout()变换方法,把一个Door2对象以参数的形式传递给它们。按照这种设计方法,Door2对象调用状态变换方法,例如click()时,将采用state.click(this)的形式。

(责任编辑 Sunny)

原作者: 俞良松

原出处: 赛迪网

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值