锁定老帖子 主题:状态模式(State)
该帖已经被评为新手帖
|
|
---|---|
作者 | 正文 |
发表时间:2010-06-17
最后修改:2010-06-18
请参看:状态模式续.
一、引言
如果你现在在设计一个交通控制程序,目前可能只有三种颜色,有可能你会将其写成如下的代码形式。 TrafficLight 代码:
package org.bestupon.dp.state; public class TrafficLight { private static enum State { RED, GREEN, YELLOW } private static State state = State.RED; public static void change() { switch (state) { case RED: System.out.println("红灯"); sleep(5000); state = State.GREEN; break; case GREEN: System.out.println("绿灯"); sleep(5000); state = State.YELLOW; break; case YELLOW: System.out.println("黄灯"); sleep(1000); state = State.RED; } } private static void sleep(int second) { try { Thread.sleep(second); } catch (InterruptedException e) { e.printStackTrace(); } } }
Client代码:
package org.bestupon.dp.state; public class Client { public static void main(String[] args) { while (true) { TrafficLight.change(); } } }
在以上的模式的设计基础上完成了功能,但是如果状态(红绿灯颜色)增多了呢?是不是还要修改源码呢?是不是还要修改switch语句呢?这样是不是整个流程会变得冗长呢?或者是状态是不间断的平凡的变换的呢?也就是说在{红、黄、绿}->{红、黄、绿、蓝}->{红、黄、绿、黑}->{红、黄、绿、其他}->{其他}这样不定的变换,是不是代码要平凡的修改呢? 我们终于看见了自己写的代码还是不够的灵活,那怎么才能让其变的灵活、适应各种情况呢?我们就从重构出发看看怎么解决我们的问题。 那么什么是重构呢?又如何样重构呢? 二、重构
重构:
1. 对软件内部结构的一种调整,目的是在不改变软件可观察行为的前提下,提高其可理解性,降低其修改成本。 2. 使用一系列重构手法,在不改变软件可观察行为的前提下,调整其结构。 3. 通俗的说法:重构是一种高效且受控的代码整理技术。
实现红绿灯之间状态的变化主要是依靠switch语句来改变状态的,在重构方法上有这么一条Replace Type Code with State/Strategy 这么一种手段。那么究竟是选择State模式还是Stratey模式呢?分析我们的问题主要是解决状态变化多端,而State就是(变化多端),Stratey(明修栈道、暗渡乘沧)。那么什么是状态模式呢?
三、状态模式
定义: 根据对象的状态不同,将具有不同的行为。 UML图:
使用时机: 当一个对象的行为取决于他的状态,并且它必须在运行时刻根据状态改变他的行为,或者一个操作中还有庞大的多分支的条件语句,并且这些分支依赖于该对象的状态时,就需要适应状态模式。 四、分析
回忆上面的代码,其有很多的分支(switch语句),伴随着状态的增加,条件分支将越来越复杂,并且所依赖每一种状态都有不同的行为(每个case都有不同的动作),所以我们可以依据状态模式重构他。 下面看看,我们如何使用状态模式(State)来重构它。 五、办法
1. 对TrafficLight 瘦身: 将 每一中状态抽象到一个类中去(LightState),为了每次都能很清楚的知道下一个状态是什么状态,我们设计change()中增加一个下次状态的LightState参数。 TrafficLight代码如下:
package org.bestupon.dp.state.refactor; /** * * @author BestUpon * @email bestupon@foxmail.com * @date 2010-6-15下午05:21:47 * @ask * @answer */ public class TrafficLight { private LightState current = null; public void set(LightState state) { this.current = state; } public void change(TrafficLight light,LightState firstState,LightState nextState) { current = firstState; current.change(this,current); } }
LightState 代码如下: package org.bestupon.dp.state.refactor; /** * * @author BestUpon * @email bestupon@foxmail.com * @date 2010-6-15下午05:21:00 * @ask * @answer */ public interface LightState { public void change(TrafficLight light,LightState nextState); } 2. 增加一层对个状态的控制层, abstract Light 类:让Light 去实现LightState接口。确保状态和操作绑定在一起。 Light 代码如下:
package org.bestupon.dp.state.refactor; /** * * @author BestUpon * @email bestupon@foxmail.com * @date 2010-6-15下午05:21:15 * @ask * @answer */ public abstract class Light implements LightState { @Override public abstract void change(TrafficLight light, LightState nextState); protected void sleep(int second) { try { Thread.sleep(second); } catch (InterruptedException e) { e.printStackTrace(); } } }
3. 让没一个真正的状态去实现Light这个抽象类.
4.客户端
package org.bestupon.dp.state.refactor; /** * * @author BestUpon * @email bestupon@foxmail.com * @date 2010-6-15下午05:23:47 * @ask * @answer */ class Client { public static void main(String[] args) { TrafficLight trafficLight = new TrafficLight(); // LightState states [] = {new RedLight(), new YellowLight(), new GreenLight(),new YellowLight() }; LightState states[] = StateHolder.getStates(); int index = 0; while (index != states.length) { trafficLight.change(trafficLight, states[index], index == states.length - 1 ? states[index] : states[index + 1]); index++; if (index == states.length) index = 0; } } }
5.为了在下次修改状态和状态次序的时候不修改客户端,将其封装在了一个工具类中StateHolder中。
package org.bestupon.dp.state.refactor; /** * * @author BestUpon * @email bestupon@foxmail.com * @date 2010-6-15下午08:11:33 * @ask * @answer */ public class StateHolder { private static LightState[] states = null; public static LightState[] getStates(){ if(null == states) { LightState[]statestemp= {new RedLight(), new YellowLight(), new GreenLight(),new YellowLight() }; states = statestemp; } return states; } } 优点 状态模式使用代码中复杂而庸长的逻辑判断语句问题得到了解决,而且状态角色将具体的状态和他对应的行为封装了起来,这使得增加一种新的状态显得十分简单。 缺点: 使用状态模式时,每个状态对应一个具体的状态类,使结构分散,逻辑不太清楚,阅读代码时比较困难。 评论: 现在线程是休眠时间直接写在了类中,本程序依然还有重构的价值!
声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |
发表时间:2010-06-17
最后修改:2010-06-17
枚举各种万能
既然你用了枚举,为啥不用枚举来写状态模式 |
|
返回顶楼 | |
发表时间:2010-06-17
最后修改:2010-06-17
iaimstar 写道 枚举各种万能
既然你用了枚举,为啥不用枚举来写状态模式 枚举虽然遍历的时候也是以自然顺序的,但是直观上数组的顺序性比较直观!也容易理解上一个状态和下一个状态的关系! |
|
返回顶楼 | |
发表时间:2010-06-17
最后修改:2010-06-17
说实话真没看出啥可读性来
trafficLight.change(trafficLight, states[index], index == states.length - 1 ? states[index] : states[index + 1]); 着看着多蛋疼啊 |
|
返回顶楼 | |
发表时间:2010-06-17
最后修改:2010-06-17
在你的Client里面
trafficLight.change(trafficLight, states[index], index == states.length - 1 ? states[index] : states[index + 1]); 这句当index==3时所指的nextState是不是不对啊 指向了currentState package org.bestupon.dp.state.refactor; /** * * @author BestUpon * @email bestupon@foxmail.com * @date 2010-6-15下午05:23:47 * @ask 状态模式客户端 * @answer */ class Client { public static void main(String[] args) { TrafficLight trafficLight = new TrafficLight(); LightState states[] = StateHolder.getStates(); int index = 0; while (true) { if(index != states.length) { trafficLight.change(trafficLight, states[index], index == states.length - 1 ? states[0] : states[index+1]); index++; }else { index=0; } } } } |
|
返回顶楼 | |
发表时间:2010-06-17
最后修改:2010-06-17
BestUpon 写道 iaimstar 写道 枚举各种万能
既然你用了枚举,为啥不用枚举来写状态模式 枚举虽然遍历的时候也是以自然顺序的,但是直观上数组的顺序性比较直观!也容易理解上一个状态和下一个状态的关系! enum Light{ 红灯(1000),黄灯(5000),绿灯(5000); //顺序调整. int sleepping; private Light(int x) { this.sleepping = x ; } public Light change(){ System.out.println(name()); sleep(); return next(); } public Light next(){ return values()[((ordinal()+1)%(values().length))]; } public static void main(String[] args) { Light light = Light.红灯; while(true){ light = light.change(); } } private void sleep() { try { Thread.sleep(this.sleepping); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } |
|
返回顶楼 | |
发表时间:2010-06-17
很不错,学习过。
|
|
返回顶楼 | |
发表时间:2010-06-17
抛出异常的爱 写道 BestUpon 写道 iaimstar 写道 枚举各种万能
既然你用了枚举,为啥不用枚举来写状态模式 枚举虽然遍历的时候也是以自然顺序的,但是直观上数组的顺序性比较直观!也容易理解上一个状态和下一个状态的关系! enum Light{ 红灯(1000),黄灯(5000),绿灯(5000); //顺序调整. int sleepping; private Light(int x) { this.sleepping = x ; } public Light change(){ System.out.println(name()); sleep(); return next(); } public Light next(){ return values()[((ordinal()+1)%(values().length))]; } public static void main(String[] args) { Light light = Light.红灯; while(true){ light = light.change(); } } private void sleep() { try { Thread.sleep(this.sleepping); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } 不错!写的相当的好!完了我再重构下! |
|
返回顶楼 | |
发表时间:2010-06-17
最后修改:2010-06-17
PS: 这个不是状态模式的例子.
要算也应该算是迭代器的一种变型.仔细看里面有next()方法的. |
|
返回顶楼 | |
发表时间:2010-06-17
lj9799616 写道 在你的Client里面
trafficLight.change(trafficLight, states[index], index == states.length - 1 ? states[index] : states[index + 1]); 这句当index==3时所指的nextState是不是不对啊 指向了currentState package org.bestupon.dp.state.refactor; /** * * @author BestUpon * @email bestupon@foxmail.com * @date 2010-6-15下午05:23:47 * @ask 状态模式客户端 * @answer */ class Client { public static void main(String[] args) { TrafficLight trafficLight = new TrafficLight(); LightState states[] = StateHolder.getStates(); int index = 0; while (true) { if(index != states.length) { trafficLight.change(trafficLight, states[index], index == states.length - 1 ? states[0] : states[index+1]); index++; }else { index=0; } } } } 目的就是在列出的状态中间循环,如果是最后一个状态的话,就返回到第一个状态在做循环!是按照前面的没有重构之前的意图有意这样写的,要不循环就停止了! |
|
返回顶楼 | |