继续浏览精彩内容
慕课网APP
程序员的梦工厂
打开
继续
感谢您的支持,我会继续努力的
赞赏金额会直接到老师账户
将二维码发送给自己后长按识别
微信支付
支付宝支付

设计模式经典-状态模式

jmesSehng
关注TA
已关注
手记 149
粉丝 424
获赞 5663
模式动机

在很多情况下,一个对象的行为取决于一个或多个动态变化的属性,这样的属性叫做状态,这样的对象叫做有状态的(stateful)对象,这样的对象状态是从事先定义好的一系列值中取出的。当一个这样的对象与外部事件产生互动时,其内部状态就会改变,从而使得系统的行为也随之发生变化。

在UML中可以使用状态图来描述对象状态的变化。

模式定义

状态模式(State Pattern) :允许一个对象在其内部状态改变时改变它的行为,对象看起来似乎修改了它的类。其别名为状态对象(Objects for States),状态模式是一种对象行为型模式。

状态模式所涉及到的角色

    环境(Context)角色,也成上下文:定义客户端所感兴趣的接口,并且保留一个具体状态类的实例。这个具体状态类的实例给出此环境对象的现有状态。

    抽象状态(State)角色:定义一个接口,用以封装环境(Context)对象的一个特定的状态所对应的行为。

    具体状态(ConcreteState)角色:每一个具体状态类都实现了环境(Context)的一个状态所对应的行为。

uml

这里写图片描述

具体例子:

策略模式和状态模式是双胞胎,在出生时才分开。你已经知道,策略模式是围绕可以互换的算法来创建成功业务的,然而,状态走的是更崇高的路,它通过改变对象内部的状态来帮助对象控制自己的行为。

题例:

万能糖果公司 我们认为糖果机的控制器需要如下图般的工作,希望你能用Java语言帮我们实现它,而且需要让设计能够尽量有弹性而且好维护,因为将来我们可能要为它增加更多的行为。
这里写图片描述

没错上图是一个状态图。

1:首先找出所有的状态:“没有25分钱”,“有25分钱”,“糖果售罄”,“售出糖果”。

2:接下来,创建一个实例变量来持有目前的状态,然后定义每个状态的值: final static int SOLD_OUT=0; final static int NO_QUARTER=1; final static int HAS_QUARTER=2; final static int SOLD=3; int state =SOLD_OUT;

final static int SOLD_OUT=0; 
final static int NO_QUARTER=1; 
final static int HAS_QUARTER=2; 
final static int SOLD=3; 
int state =SOLD_OUT;

3:现在,我们将所有系统中可以发生的动作整合起来: “投入25分钱”,“退回25分钱”,“转动曲柄”,“发放糖果” 这些动作是糖果机的接口,这是你能对糖果机做的事情, 调用任何一个动作都会造成状态的转换, 发放糖果更多是糖果机的内部动作,机器自己调用自己。

4:现在,我们创建了一个类,它的作用就像是一个状态机,第每一个动作,我们都创建了一个对应的方法,这些方法利用条件语句来决定在每个状态内什么行为是恰当的。比如对“投入25分钱”这个动作来说,我们可以把对应方法写成下面的样子:
这里写图片描述

新的设计

不要维护我们现有的代码,我们重写它以便于将状态对象封装在各自的类中,然后在动作发生时委托给当前状态。
1:首先,我们定义一个State接口,在这个接口内,糖果机的每个动作都有一个对应的方法。

2:然后为机器中的每个状态实现状态类,这些类将负责在对应的状态下进行机器的行为。

3:最后,我们要摆脱旧的条件代码,取而代之的方法是,将动作委托到状态类。 现在我们要把一个状态的所有行为放在一个类中,这么一来我们将行为局部化了,并使得事情更容易改变和理解。

定义状态接口和类 我们先完成第一个版本的糖果机的重新实现之后,再回来处理添加“赢家”的修改。 首先让我们创建一个State接口,所有的状态都必须实现这个接口:

interface State
{ void insertQuarter(); 
void ejectQuarter();
 void turnCrank(); 
 void dispense(); }

这里写图片描述

重新改造糖果机
class GumballMachine_ 
{ State soldOutState; 
State noQuarterState; 
State hasQuarterState; 
State soldState; 
State state = soldState;
 int count = 0; 
 public GumballMachine_(int count) 
 { soldOutState = new SoldOutState(this); noQuarterState = new NoQuarterState(this); hasQuarterState = new HasQuarterState(this); soldOutState = new SoldState(this); 
 this.count = count; 
 if (count > 0) 
 { state = noQuarterState; } } 
 public void insertQuarter()
  { state.insertQuarter(); } 
  public void ejectQuarter() 
  { state.ejectQuarter(); } 
  public void turnCrank(){ state.turnCrank(); state.dispense(); }
   void setState(State state){ this.state=state; }
    void releaseBall(){ System.out.println(); if(count!=0){ count=count-1; } }
     public State getSoldState() { return soldState; } public State getHasQuarterState() { return hasQuarterState; } 
     public State getNoQuarterState() { return noQuarterState; } 
     public State getSoldOutState() { return soldOutState; } } 
状态类举例
class HasQuarterState implements State { GumballMachine_ gumballMachine; 
public HasQuarterState(GumballMachine_ gumballMachine) { this.gumballMachine = gumballMachine; }
 @Override public void insertQuarter() { System.out.println("这是一个对此状态不恰当的动作"); } @Override public void ejectQuarter() { System.out.println("退出顾客的25分钱,并将状态转换到NoQuarterState状态"); gumballMachine.setState(gumballMachine.getNoQuarterState()); }
@Override public void turnCrank() { System.out.println("当曲柄转动,我们将状态转换到SoldState"); gumballMachine.setState(gumballMachine.getSoldState()); }
@Override public void dispense() { System.out.println("这是次状态的另一个不恰当动作"); } } 
策略模式和状态模式的区别
但是这两个模式的差别在于它们的“意图” 以状态模式而言

我们将一群行为封装在状态对象中,context的行为随时可委托到那些状态对象中的一个,随着时间而流逝,当前状态在状态对象集合中游走改变,以反映出context内部的状态,因此,context的行为也会跟着改变,但是context的客户对于状态对象了解不多,甚至根本是浑然不觉。

而策略模式而言,客户通常主动指定Context所要组合的策略对象时哪一个。现在,固然策略模式让我们具有弹性,能够在运行时改变策略,但对于某个context对象来说,通常都只有一个最适当的策略对象。 一般的,我们把策略模式想成是除了继承之外的一种弹性替代方案,如果你使用继承定义了一个类的行为,你将被这个行为困住,是指要修改它都很难,有了策略模式,你可以通过组合不同的对象来改变行为。

我们把状态模式想成是不用咋icontext中防止旭东条件判断的替代方案,通过将行为包装进状态对象中,你可以通过在context内简单地改变状态对象来改变context的行为。

优点:

封装了转换规则。
枚举可能的状态,在枚举状态之前需要确定状态种类。
将所有与某个状态有关的行为放到一个类中,并且可以方便地增加新的状态,只需要改变对象状态即可改变对象的行为。
允许状态转换逻辑与状态对象合成一体,而不是某一个巨大的条件语句块。
可以让多个环境对象共享一个状态对象,从而减少系统中对象的个数。

缺点:

状态模式的使用必然会增加系统类和对象的个数。
状态模式的结构与实现都较为复杂,如果使用不当将导致程序结构和代码的混乱。
状态模式对“开闭原则”的支持并不太好,对于可以切换状态的状态模式,增加新的状态类需要修改那些负责状态转换的源代码,否则无法切换到新增状态;而且修改某个状态类的行为也需修改对应类的源代码。

适用环境

对象的行为依赖于它的状态(属性)并且可以根据它的状态改变而改变它的相关行为。

代码中包含大量与对象状态有关的条件语句,这些条件语句的出现,会导致代码的可维护性和灵活性变差,不能方便地增加和删除状态,使客户类与类库之间的耦合增强。在这些条件语句中包含了对象的行为,而且这些条件对应于对象的各种状态。

我的微信号:rdst6029930

欢迎交流

打开App,阅读手记
6人推荐
发表评论
随时随地看视频慕课网APP

热门评论

原谅我看不懂

000.00.0.00.0.0...............................................


查看全部评论