5.8 STATE(状态) — 对象行为型模式

1 意图

允许一个其对象在其内部状态改变时改变它的行为,对象看起来似乎修改了它的类。

2 别名

状态对象(Objects For States)

3 动机

考虑一个表示网络连接的类TCPConnection。一个TCPConnection对象的状态处于若干不同状态之一:连接已建立(Established)、正在监听(Listening)、连接已关闭(Closed)。当一个TCPConnection对象收到其他对象的请求时,它根据自身的当前状态作出不同的反应。例如,一个Open请求的结果依赖于该连接是处于连接已关闭状态还是连接已建立状态。State模式描述了TCPConnection如何在每一种状态下表现出不同的行为。

这一模式的关键思想是引入了一个称为TCPState的抽象类来表示网络的连接状态。TCPState类为各表示不同的操作状态的子类声明了一个公共接口。TCPState的子类实现与特定状态相关的行为。例如,TCPEstablished和TCPClosed类分别实现了特定于TCPConnection的连接已建立状态和连接已关闭状态的行为。


image.png

TCPConnection类维护一个表示TCP连接当前状态的状态对象(一个TCPState子类的实例)。TCPConnection类将所有与状态相关的请求委托给这个状态对象。TCPConnection使用它的TCPState子类实例来执行特定于连接状态的操作。
一旦连接状态改变,TCPConnection对象就会改变它所使用的状态对象。例如当连接从已建立状态转为已关闭状态时,TCPConnection会用一个TCPClosed的实例来代替原来的TCPEstablished的实例。

4 适用性

在下面的两种情况下均可使用State模式:

  • 一个对象的行为取决于它的状态,并且它必须在运行时刻根据状态改变它的行为;
  • 一个操作中含有庞大的多分支的条件语句,且这些分支依赖于该对象的状态。这个状态通常用一个或多个枚举常量表示。通常 , 有多个操作包含这一相同的条件结构。state模式将每一个条件分支放入一个独立的类中。这使得你可以根据对象自身的情况将对象的状态作为一个对象,这一对象可以不依赖于其他对象而独立变化。
5 结构
image.png
6 参与者
  • Context(环境,如TCPConnection)
    ——定义客户感兴趣的接口
    ——维护一个ConcreteState子类的实例,这个实例定义当前的状态
  • State(状态,如TCPState)
    ——定义一个接口以封装与Context的一个特定状态相关的行为
  • ConcreteState subclasses(具体状态子类,如TCPEstablished,TCPListen,TCPClosed)
    ——每一子类实现一个与Context的一个状态相关的行为
7 协作
  • Context将与状态相关的请求委托给当前的ConcreteState对象处理;
  • Context可将自身作为一个参数传递给处理该请求的状态对象。这使得状态对象在必要时可访问Context;
  • Context是客户使用的主要接口。客户可用状态对象来配置一个Context,一旦一个Context配置完毕, 它的客户不再需要直接与状态对象打交道。
  • Context或ConcreteState子类都可决定哪个状态是另外哪一个的后继者,以及是在何种条件下进行条件转换。
8 效果

State模式有下面一些效果:

  • 1 它将与特定状态相关的行为局部化,并且将不同状态的行为分割开来
  • 2 它使得状态转换显式化:当一个对象仅以内部数据值来定义当前状态时 , 其状态仅表现为对一些变量的赋值,这不够明确。为不同的状态引入独立的对象使得转换变得更加明确。而且, State对象可保证Context不会发生内部状态不一致的情况,因为从Context的角度看,状态转换是原子的 — 只需重新绑定一个变量,(即Context的State对象变量),而无需为多个变量赋值。
  • 3 State对象可被共享:如果State对象没有实例变量 — 即它们表示的状态完全以它们的类型来编码 — 那么各Context对象可以共享一个State对象。当状态以这种方式被共享时 , 它们必然是没有内部状态, 只有行为的轻量级对象。
9 实现

实现State模式有多方面的考虑:

  • 1 谁定义状态转换:State模式不指定哪一个参与者定义状态转换准则。如果该准则是固定的, 那么它们可在Context中完全实现。然而若让State子类自身指定它们的后继状态以及何时进行转换 , 通常更灵活更合适。这需要Context增加一个接口 , 让State对象显式地设定Context的当前状态。
    用这种方法分散转换逻辑可以很容易地定义新的State子类来修改和扩展该逻辑。 这样做的一个缺点是,一个State子类至少拥有一个其他子类的信息 , 这就再各子类之间产生了实现依赖。
  • 2 基于表的另一种方法:在C++ Programming Style中,Cargil描述了另一种将结构加载在状态驱动的代码上的方法 : 他使用表将输入映射到状态转换。对每一个状态 , 一张表将每一个可能的输入映射到一个后继状态。实际上 , 这种方法将条件代码 (和State模式下的虚函数)映射为一个查找表。
    表的主要好处是它们的规则性 : 你可以通过更改数据而不是更改程序代码来改变状态转换的准则。然而它也有一些缺点:
    • 对表的查找通常不如(虚)函数调用效率高;
    • 用统一的、表格的形式表示转换逻辑使得转换准则变得不够明确而难以理解;
    • 通常难以加入伴随状态转换的一些动作。表驱动的方法描述了状态和它们之间的转换,但必须扩充这个机制以便在每一个转换上能够进行任意的计算。
      表驱动的状态机和State模式的主要区别可以被总结如下: State模式对与状态相关的行为进行建模, 而表驱动的方法着重于定义状态转换。
  • 3 创建和销毁State对象:一个常见的值得考虑的实现上的权衡是 , 究竟是( 1 )仅当需要State对象时才创建它们并随后销毁它们,还是 ( 2 )提前创建它们并且始终不销毁它们。
    当将要进入的状态在运行时是不可知的 , 并且上下文不经常改变状态时 , 第一种选择较为可取。这种方法避免创建不会被用到的对象 , 如果State对象存储大量的信息时这一点很重要。当状态改变很频繁时, 第二种方法较好。在这种情况下最好避免销毁状态 , 因为可能很快再次需要用到它们。此时可以预先一次付清创建各个状态对象的开销 , 并且在运行过程中根本不存在销毁状态对象的开销。但是这种方法可能不太方便 , 因为Context必须保存对所有可能会进入的那些状态的引用。
  • 4 使用动态继承:改变一个响应特定请求的行为可以用在运行时刻改变这个对象的类的办法实现, 但这在大多数面向对象程序设计语言中都是不可能的。 Self 和其他一些基于委托的语言却是例外,它们提供这种机制 , 从而直接支持State模式。Self 中的对象可将操作委托给其他对象以达到某种形式的动态继承。在运行时刻改变委托的目标有效地改变了继承的结构。这一机制允许对象改变它们的行为,也就是改变它们的类。
10 代码示例

github地址

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,542评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,596评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,021评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,682评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,792评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,985评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,107评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,845评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,299评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,612评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,747评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,441评论 4 333
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,072评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,828评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,069评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,545评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,658评论 2 350

推荐阅读更多精彩内容