状态模式(Stats Pattern)
来源:互联网 发布:软件开发培训学费 编辑:程序博客网 时间:2024/06/07 06:24
在实际项目中,应用程序往往需要根据不同的情况做出不同的处理。在开发工程中,需要考虑到各种场景、分支,常常会使用到if..else或者switch case等分支,通过判断条件处理不同的情况。当这种判断变得复杂的时候,分支增多,代码量增多,对代码的维护和可读性、扩展性带来了不好的影响。这种时候就可以考虑使用状态模式。
状态模式:
允许一个对象在其内部状态改变时改变它的行为。对象看起来似乎修改了它的类。
在很多情况下,一个对象的行为取决于一个或多个动态变化的属性,这样的属性叫做状态,这样的对象叫做有状态的(stateful)对象,这样的对象状态是从事先定义好的一系列值中取出的。当一个这样的对象与外部事件产生互动时,其内部状态就会改变,从而使得系统的行为也随之发生变化。
例子1:按钮来控制一个电梯的状态,一个电梯开们,关门,停,运行。每一种状态改变,都有可能要根据其他状态来更新处理。例如,开门状体,你不能在运行的时候开门,而是在电梯定下后才能开门。
例子2:我们给一部手机打电话,就可能出现这几种情况:用户开机,用户关机,用户欠费停机,用户消户等。 所以当我们拨打这个号码的时候:系统就要判断,该用户是否在开机且不忙状态,又或者是关机,欠费等状态。但不管是那种状态我们都应给出对应的处理操作。
适用场景
1.一个对象的行为取决于它的状态,并且它必须在运行时刻根据状态改变它的行为。
2.一个操作中含有庞大的多分支结构,并且这些分支决定于对象的状态。
角色说明
1.环境类(Context): 定义客户感兴趣的接口。维护一个ConcreteState子类的实例,这个实例定义当前状态。
2.抽象状态类(State): 定义一个接口以封装与Context的一个特定状态相关的行为。
3.具体状态类(ConcreteState): 每一子类实现一个与Context的一个状态相关的行为。
类图
代码示例
状态模式最出名的莫过于状态机了,这里用代码模拟一个娃娃机(Context)。简单模拟一下娃娃机的状态分别为:空仓,等待投币,操作中三个具体状态(ConcreteStats);用户对娃娃机的行为包括:补货,投币,摇杆操作,抓娃娃操作。
先写一段伪代码,看看不用状态模式时是怎么写的
switch (事件){ case 投币: if(等待状态){ //可以投币 }else if(操作中状态){ //不能投币 }else { //空仓状态,不能投币 } return; case 摇杆: //略………… case 抓娃娃: //略………… case 补货: //略…………}为了缩短代码长度,写得比较粗糙,不过大意就是:不同的事件需要判断娃娃机当前的状态,然后根据状态做出相应的响应。如果这段代码实实在在的都写完写好了,相比是一大段判断语句,一大段代码,可读性、扩展性都不太好。
下面我们试着使用状态模式完成娃娃机
Stats:
/** * 定义了4个娃娃机的行为 * Created by yangjiachang on 2016/9/12. */public abstract class Stats { //娃娃机实例 protected StatsMachine machine; /** * 投币 */ public abstract void insertCoins(); /** * 摇杆 */ public abstract void rock(); /** * 抓娃娃 */ public abstract void get(); /** * 补货 */ public abstract void supplement(int count);}
ConcreteStats:
/** * 空仓状态,没有娃娃了 * Created by yangjiachang on 2016/9/12. */public class EmptyStats extends Stats { public EmptyStats(StatsMachine machine){ this.machine = machine; } public EmptyStats(){ } public void insertCoins() { System.out.println("没有娃娃了,不能投币,钱退还给你"); } public void rock() { System.out.println("摇杆无效"); } public void get() { System.out.println("不能抓娃娃"); } public void supplement(int count) { machine.setCount(machine.getCount() + count); machine.setStats(machine.getWaitStats()); System.out.println("补货完成,又有" + machine.getCount()+"个娃娃了"); }}
/** * 操作摇杆状态,抓娃娃 * Created by yangjiachang on 2016/9/12. */public class OperateStats extends Stats { public OperateStats(StatsMachine machine){ this.machine = machine; } public OperateStats(){ } public void insertCoins() { System.out.println("已经投过钱币了,不要再投了,多的钱退给你"); } /** * 摇杆 */ public void rock() { System.out.println("寻找一个最好的娃娃"); } /** * 抓娃娃 */ public void get() { System.out.println("正在抓取娃娃……"); int count = machine.getCount()-1; if(count > 0){ machine.setCount(count); machine.setStats(machine.getWaitStats()); }else { machine.setCount(count); machine.setStats(machine.getEmptyStats()); } System.out.println("抓到娃娃,剩余:"+machine.getCount()); } public void supplement(int count) { System.out.println("用户正在操作,请稍后补货"); }}
/** * 等待投币状态 * Created by yangjiachang on 2016/9/12. */public class WaitStats extends Stats { public WaitStats(StatsMachine machine){ this.machine = machine; } public WaitStats(){} public void insertCoins() { System.out.println("投币成功,你可以开始抓娃娃了"); machine.setStats(machine.getOperateStats()); } public void rock() { System.out.println("摇杆无效,请先投币"); } public void get() { System.out.println("不能抓娃娃,请先投币"); } public void supplement(int count) { machine.setCount(machine.getCount() + count); System.out.println("补货完成,又有" + machine.getCount()+"个娃娃了"); }}Context:
/** * 定义一个状态机,这里就是娃娃机 * Created by yangjiachang on 2016/9/12. */public class StatsMachine { //娃娃机存在的三个状态 private Stats emptyStats = new EmptyStats(this); private Stats waitStats = new WaitStats(this); private Stats operateStats = new OperateStats(this); //记录当前状态 private Stats stats; //娃娃数量 private int count; //初始化 public StatsMachine(int count){ if (count <= 0){ this.count = 0; this.stats = emptyStats; }else { this.count = count; this.stats = waitStats; } } /* 行为start */ public void insertCoins(){ stats.insertCoins(); } public void rock(){ stats.rock(); } public void get(){ stats.get(); } public void supplement(int count){ stats.supplement(count); } /* 行为end */ //setting getting public Stats getStats() { return stats; } public void setStats(Stats stats) { this.stats = stats; } public Stats getEmptyStats() { return emptyStats; } public Stats getWaitStats() { return waitStats; } public Stats getOperateStats() { return operateStats; } public int getCount() { return count; } public void setCount(int count) { this.count = count; }}测试方法:
public static void main(String[] args) { int count = 2; StatsMachine machine = new StatsMachine(count); for(int i=0 ; i<count+1 ; i++){ System.out.println("当前状态:"+machine.getStats().getClass().getSimpleName()); machine.insertCoins(); System.out.println("当前状态:" + machine.getStats().getClass().getSimpleName()); machine.rock(); System.out.println("当前状态:" + machine.getStats().getClass().getSimpleName()); machine.get(); System.out.println(); } machine.supplement(3); System.out.println("剩余数量:" + machine.getCount());}
执行结果:
当前状态:WaitStats
投币成功,你可以开始抓娃娃了
当前状态:OperateStats
寻找一个最好的娃娃
当前状态:OperateStats
正在抓取娃娃……
抓到娃娃,剩余:1
当前状态:WaitStats
投币成功,你可以开始抓娃娃了
当前状态:OperateStats
寻找一个最好的娃娃
当前状态:OperateStats
正在抓取娃娃……
抓到娃娃,剩余:0
当前状态:EmptyStats
没有娃娃了,不能投币,钱退还给你
当前状态:EmptyStats
摇杆无效
当前状态:EmptyStats
不能抓娃娃
补货完成,又有3个娃娃了
剩余数量:3
对状态模式的理解:
1.它将与特定状态相关的行为局部化,并且将不同状态的行为分割开来
将状态封装成对象,由抽象状态类定义所有的行为;将Context的每种状态定义成一个具体状态类,并实现该状态下各种行为的实现。所以通过定义新的子类很容易扩展新的状态和转移状态(例如投币行为将娃娃机由等待投币状态变为可操作状态),并实现该新状态下的行为。这种方式避免了过多的if..else或者switch case语句带来的分支代码,并将各自状态的逻辑封装到一个具体状态类中,对于理解该状态下什么能做、做了什么提供了更好的可读性。
2.显示的转换状态,增强可读性
实际应用中,状态的判断往往的多个变量的值的判断,在换货状态时,需要对多个变量赋值,这样的方式加深了理解的难道。通过状态模式,将状态封装在一个类中,在判断与转换的过程中,更加直截了当
3.状态可以共享
如果有必要,可以多个Context共享同一个Stats状态,也可以减少
4.将状态封装成对象,势必会增加系统类和对象个数,运用不当反而会显得结构和代码混乱
- 状态模式(Stats Pattern)
- State Pattern(状态模式)
- State Pattern(状态模式)
- 状态模式(Strategy Pattern)
- 状态模式(State Pattern)
- 状态模式(State Pattern)
- 状态模式(State Pattern)
- 状态模式(State Pattern)
- 状态模式(State Pattern)
- 状态模式(State Pattern)
- 状态模式(State Pattern)
- 状态模式(State Pattern)
- 状态模式(State Pattern)
- 【设计模式】状态模式(State Pattern)
- State Pattern状态模式(三)
- 状态模式(State Design Pattern)
- 状态模式(State Pattern)
- 状态模式(State Pattern)
- C++11 并发指南一(C++11 多线程初探)
- 会计算的calc()
- Raptor实践参考:求和
- 循环的问题,当涉及到自身的增减问题时,
- web.xml里<filter-mapping>中的<dispatcher>作用
- 状态模式(Stats Pattern)
- 【Android 4.0 4.1 4.2】页面相关图片CSS absolute
- java 中 mongodb的各种操作 模糊查询 精确查询 等等
- sql查询简单总结
- RequestDispatcher提供两个方法:forward,include有什么区别
- 【GLSL教程】(一)图形流水线
- C++学习路线及书籍推荐
- 如何用PHP获取百度地图
- 怎样用swift制作一个滑出的导航控制面板