快捷搜索:

您的位置:澳门新葡4473网站 > 新葡亰平台娱乐 > 设计模式 ( 十七) 状态模式State(对象行为型)

设计模式 ( 十七) 状态模式State(对象行为型)

发布时间:2020-01-24 07:25编辑:新葡亰平台娱乐浏览(131)

    /********************有限状态机自动机***********************/
    状态图--一个图的数据结构!
    1.while + switch;
    2.状态机:就是指定系统的所有可能的状态及状态间跳转的条件,然后设一个初始状态输入给这台机器,机器就会自动运转,或最后处于终止状态,或在某一个状态不断循环。
    游戏中状态切换是很频繁的。 可能以前要切换状态就是if~else,或者设标志,但这些都不太结构化, 如果把它严格的设为一种标准的状态机,会清楚的多。

    设计模式 ( 十七) 状态模式State(对象行为型)

    比如控制一扇门的运动, 初始时门是关的, 当有力作用在门上时, 门开始慢慢打开,力的作用完后,门渐渐停止不动, 当有反向的力时,门又渐渐关上, 知道回到初始关的状态。 这个你会怎么来编程实现呢。 似乎很麻烦, 的确,没有状态机的思想时会很烦,设很多标志,一堆if条件。
    用状态机的话,不只是代码更清晰, 关键是更符合逻辑和自然规律, 不同状态不同处理, 满足条件则跳转到相关状态。

    1.概述

    伪码如下:
    enum
    {
     CLOSED, // 关上状态
     CLOSING, // 正在关状态
     OPENED, // 打开状态
     OPENING, // 正在开的状态
    }doorState = CLOSED; // 初始为关

     

    Update()
    {
         switch(doorState)
         case CLOSED:
              if (有人推门)
                   doorState = OPENING; // 跳转到正在开状态
         break;
         case OPENING:
              door.Rotation += DeltaAngle; // 门的旋转量递增
              if (门的速度为零) / / 力的作用已去
                   doorState = OPENED; // 跳转到开状态
        break;
        case OPENED:
             if (有人关门)
                  doorState = CLOSING;
       break;
       case CLOSING:
             door.Rotation -= DeltaAngle; // 门的旋转量递减
             if (门的旋转角度减为零)
                  doorState = CLOSED; // 门已关上
        break;
    }

    在软件开发过程中,应用程序可能会根据不同的情况作出不同的处理。最直接的解决方案是将这些所有可能发生的情况全都考虑到。然后使用if... ellse语句来做状态判断来进行不同情况的处理。但是对复杂状态的判断就显得“力不从心了”。随着增加新的状态或者修改一个状体(if else(或switch case)语句的增多或者修改)可能会引起很大的修改,而程序的可读性,扩展性也会变得很弱。维护也会很麻烦。那么我就考虑只修改自身状态的模式。

    例子1:按钮来控制一个电梯的状态,一个电梯开们,关门,停,运行。每一种状态改变,都有可能要根据其他状态来更新处理。例如,开门状体,你不能在运行的时候开门,而是在电梯定下后才能开门。

    例子2:我们给一部手机打电话,就可能出现这几种情况:用户开机,用户关机,用户欠费停机,用户消户等。 所以当我们拨打这个号码的时候:系统就要判断,该用户是否在开机且不忙状态,又或者是关机,欠费等状态。但不管是那种状态我们都应给出对应的处理操作。

    // 而绘制代码几乎不用怎么变, 门就是会严格按照状态机的指示去运动, 该停就会停
    Render()
    {
         RotateView(door.Rotation);
         DrawDoor(door.Position);
    }

     

    这是一个简单但很典型的例子, 状态机的应用太多了。
    就说一个基本游戏的运转: (用到了一个状态然后还有子状态)
    UpdateGame()
    BEGIN;
       switch(gameState)
       case 等待选择菜单: //它有三个子状态。
       if (选择菜单项 == 开始)
       {
           游戏初始;
          gameState = 开始游戏
       }
       if (选择菜单项 == 选项)
          gameState = 设置
       if (选择菜单项 == 退出)
           gameState = 退出

    2.问题

     case 开始:
     
     游戏运行;
     if (用户按退出键)
     gameState = 等待选择菜单 ;
     ...其他的状态跳转处理;
     case 退出:
     释放资源;
     退出;
     case 设置:
     分别处理不同的选项, 跳转不同的子状态;
     case .... // 其他状态的处理
     
    END;

     

    某一个状态可以包含更多的子状态, 这样最好是同一层次的状态设为一个枚举, 并分到另一个switch处理
    如 enum STATES{state1, state2, state3}; state2又包含若干状态
    则再定义enum SUB_STATE2{sub_state2_1, sub_state2_2, sub_state2_3,};

    对象如何在每一种状态下表现出不同的行为?

    想很多基本的渲染效果, 如淡入淡出, 闪烁等等, 用状态的思想会事半功倍, 思路也更清晰。

     

    其实像Opengl, Direct3D这样的渲染引擎本身就是状态机, 当你设置渲染的状态, 这台机器就保持这个状态进行渲染工作,如保持光照位置,保持片元颜色, 直到你再次改变它的状态。

    3.解决方案

    状态机的应用实在太广, 相关理论也很多, 最近上课学的随机过程里也讲到一些, 数字电路里的时序逻辑器件也是用状态机来描述。 这些不必多说了。

     

    总之, 用状态机的角度去看待问题, 往往能把比较复杂的系统分解为能单独处理的众多子状态, 处理也会得心应手。希望大家多用它, 很好的东西。

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

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

     

    二、
    推荐这个:[程序员杂志2004.8月刊_state模式和composite模式实现的状态机引擎]

     

    个人感觉状态机的几个不同实现阶段:
    1、switch/case 最原始的实现方式,是很多的c程序员习惯采用的方式。

    4.适用性

    2、查找表[状态、事件、动作],稍微做了一点改进。有点类似MFC的雏形。

     

    3、在以上基础上做的一些改进或者变体。
    [比如用一个栈结构,激活的状态位于栈顶,自动的映射事件和动作的对应,再或者通过一些巧妙的宏等手段进行包装。但是线性结构在实际中使用比较受限、过于技巧性的宏比较难于理解...]

     

    在下面的两种情况下均可使用State模式:
    1) • 一个对象的行为取决于它的状态, 并且它必须在运行时刻根据状态改变它的行为。
    2) • 代码中包含大量与对象状态有关的条件语句:一个操作中含有庞大的多分支的条件(if else(或switch case)语句,且这些分支依赖于该对象的状态。这个状态通常用一个或多个枚举常量表示。通常 , 有多个操作包含这一相同的条件结构。 State模式将每一个条件分支放入一个独立的类中。这使得你可以根据对象自身的情况将对象的状态作为一个对象,这一对象可以不依赖于其他对象而独立变化。

     

    4、面向对象的设计下、灵活运用模式。如上面给出的链接。重用性和灵活性方面都有不错的表现。沿袭类似的设计思路、根据实际开发内容进行改造后利用

     

    5.结构

     

    图片 1

     

    6.模式的组成

     

    环境类(Context):  定义客户感兴趣的接口。维护一个ConcreteState子类的实例,这个实例定义当前状态。
    抽象状态类(State):  定义一个接口以封装与Context的一个特定状态相关的行为。
    具体状态类(ConcreteState):  每一子类实现一个与Context的一个状态相关的行为。

     

    7.效果

     

    State模式有下面一些效果:
    状态模式的优点:
    1 ) 它将与特定状态相关的行为局部化,并且将不同状态的行为分割开来: State模式将所有与一个特定的状态相关的行为都放入一个对象中。因为所有与状态相关的代码都存在于某一个State子类中, 所以通过定义新的子类可以很容易的增加新的状态和转换。另一个方法是使用数据值定义内部状态并且让 Context操作来显式地检查这些数据。但这样将会使整个Context的实现中遍布看起来很相似的条件if else语句或switch case语句。增加一个新的状态可能需要改变若干个操作, 这就使得维护变得复杂了。State模式避免了这个问题, 但可能会引入另一个问题, 因为该模式将不同状态的行为分布在多个State子类中。这就增加了子类的数目,相对于单个类的实现来说不够紧凑。但是如果有许多状态时这样的分布实际上更好一些, 否则需要使用巨大的条件语句。正如很长的过程一样,巨大的条件语句是不受欢迎的。它们形成一大整块并且使得代码不够清晰,这又使得它们难以修改和扩展。 State模式提供了一个更好的方法来组织与特定状态相关的代码。决定状态转移的逻辑不在单块的 i f或s w i t c h语句中, 而是分布在State子类之间。将每一个状态转换和动作封装到一个类中,就把着眼点从执行状态提高到整个对象的状态。这将使代码结构化并使其意图更加清晰。

    2) 它使得状态转换显式化: 当一个对象仅以内部数据值来定义当前状态时 , 其状态仅表现为对一些变量的赋值,这不够明确。为不同的状态引入独立的对象使得转换变得更加明确。而且, State对象可保证Context不会发生内部状态不一致的情况,因为从 Context的角度看,状态转换是原子的—只需重新绑定一个变量(即Context的State对象变量),而无需为多个变量赋值

    3) State对象可被共享 如果State对象没有实例变量—即它们表示的状态完全以它们的类型来编码—那么各Context对象可以共享一个State对象。当状态以这种方式被共享时, 它们必然是没有内部状态, 只有行为的轻量级对象。

    状态模式的缺点:
    1) 状态模式的使用必然会增加系统类和对象的个数。
    2) 状态模式的结构与实现都较为复杂,如果使用不当将导致程序结构和代码的混乱。

     

    8.实现

    我们用电梯的例子来说明:

    简单地实现代码:

     

    [php] view plain data-mod="popu_168"> data-mod="popu_168"> copy

     

    data-mod="popu_169"> print?

    1. <?php  
    2. abstract  class="keyword">class ILift {  
    3.     //电梯的四个状态  
    4.     const OPENING_STATE = 1;   class="comment">//门敞状态  
    5.     const CLOSING_STATE = 2;   class="comment">//门闭状态  
    6.     const RUNNING_STATE = 3;   class="comment">//运行状态  
    7.     const STOPPING_STATE = 4;  class="comment">//停止状态;  
    8.   
    9.       
    10.     //设置电梯的状态  
    11.     public  class="keyword">abstract  class="keyword">function setState( class="vars">$state);  
    12.   
    13.     //首先电梯门开启动作  
    14.     public  class="keyword">abstract  class="keyword">function open();  
    15.   
    16.      class="comment">//电梯门有开启,那当然也就有关闭了  
    17.     public  class="keyword">abstract  class="keyword">function close();  
    18.   
    19.     //电梯要能上能下,跑起来  
    20.     public  class="keyword">abstract  class="keyword">function run();  
    21.   
    22.      class="comment">//电梯还要能停下来,停不下来那就扯淡了  
    23.     public  class="keyword">abstract  class="keyword">function stop();  
    24. }  
    25.   
    26. /** 
    27.  * 电梯的实现类  
    28.  */   
    29. class Lift  class="keyword">extends  ILift {  
    30.     private  class="vars">$state;  
    31.   
    32.     public  class="keyword">function setState( class="vars">$state) {  
    33.         $this->state =  class="vars">$state;  
    34.     }  
    35.     //电梯门关闭  
    36.     public  class="keyword">function close() {  
    37.         //电梯在什么状态下才能关闭  
    38.         switch( class="vars">$this->state){  
    39.              class="keyword">case ILift::OPENING_STATE:   class="comment">//如果是则可以关门,同时修改电梯状态  
    40.                  class="vars">$this->setState(ILift::CLOSING_STATE);  
    41.             break;  
    42.              class="keyword">case ILift::CLOSING_STATE:   class="comment">//如果电梯就是关门状态,则什么都不做  
    43.                 //do nothing;  
    44.                 return ;  
    45.             break;  
    46.              class="keyword">case ILift::RUNNING_STATE:  class="comment">//如果是正在运行,门本来就是关闭的,也说明都不做  
    47.                 //do nothing;  
    48.                 return ;  
    49.             break;  
    50.              class="keyword">case ILift::STOPPING_STATE:   class="comment">//如果是停止状态,本也是关闭的,什么也不做  
    51.                 //do nothing;  
    52.                 return ;  
    53.             break;  
    54.         }  
    55.                 echo  class="string">'Lift colse <br>';  
    56.     }  
    57.   
    58.     //电梯门开启  
    59.     public  class="keyword">function open() {  
    60.         //电梯在什么状态才能开启  
    61.         switch( class="vars">$this->state){  
    62.              class="keyword">case ILift::OPENING_STATE:  class="comment">//如果已经在门敞状态,则什么都不做  
    63.                 //do nothing;  
    64.                 return ;  
    65.             break;  
    66.              class="keyword">case ILift::CLOSING_STATE:  class="comment">//如是电梯时关闭状态,则可以开启  
    67.                  class="vars">$this->setState(ILift::OPENING_STATE);  
    68.             break;  
    69.              class="keyword">case ILift::RUNNING_STATE:  class="comment">//正在运行状态,则不能开门,什么都不做  
    70.             //do nothing;  
    71.                 return ;  
    72.             break;  
    73.              class="keyword">case ILift::STOPPING_STATE:  class="comment">//停止状态,淡然要开门了  
    74.                  class="vars">$this->setState(ILift::OPENING_STATE);  
    75.             break;  
    76.         }  
    77.         echo  class="string">'Lift open <br>';  
    78.     }  
    79.     ///电梯开始跑起来  
    80.     public  class="keyword">function run() {  
    81.         switch( class="vars">$this->state){  
    82.              class="keyword">case ILift::OPENING_STATE:  class="comment">//如果已经在门敞状态,则不你能运行,什么都不做  
    83.                 //do nothing;  
    84.                 return ;  
    85.             break;  
    86.              class="keyword">case ILift::CLOSING_STATE:  class="comment">//如是电梯时关闭状态,则可以运行  
    87.                  class="vars">$this->setState(ILift::RUNNING_STATE);  
    88.             break;  
    89.              class="keyword">case ILift::RUNNING_STATE:  class="comment">//正在运行状态,则什么都不做  
    90.                 //do nothing;  
    91.                 return ;  
    92.             break;  
    93.              class="keyword">case ILift::STOPPING_STATE:  class="comment">//停止状态,可以运行  
    94.                  class="vars">$this->setState(ILift::RUNNING_STATE);  
    95.         }  
    96.         echo  class="string">'Lift run <br>';  
    97.     }  
    98.   
    99.     //电梯停止  
    100.     public  class="keyword">function stop() {  
    101.         switch( class="vars">$this->state){  
    102.              class="keyword">case ILift::OPENING_STATE:  class="comment">//如果已经在门敞状态,那肯定要先停下来的,什么都不做  
    103.                 //do nothing;  
    104.                 return ;  
    105.             break;  
    106.              class="keyword">case ILift::CLOSING_STATE:  class="comment">//如是电梯时关闭状态,则当然可以停止了  
    107.                  class="vars">$this->setState(ILift::CLOSING_STATE);  
    108.             break;  
    109.              class="keyword">case ILift::RUNNING_STATE:  class="comment">//正在运行状态,有运行当然那也就有停止了  
    110.                  class="vars">$this->setState(ILift::CLOSING_STATE);  
    111.             break;  
    112.              class="keyword">case ILift::STOPPING_STATE:  class="comment">//停止状态,什么都不做  
    113.                 //do nothing;  
    114.                 return ;  
    115.             break;  
    116.         }  
    117.         echo  class="string">'Lift stop <br>';  
    118.     }  
    119.       
    120. }  
    121. $lift =  class="keyword">new Lift();   
    122.      
    123. //电梯的初始条件应该是停止状态   
    124. class="vars">$lift->setState(ILift::STOPPING_STATE);   
    125. //首先是电梯门开启,人进去   
    126. $lift->open();   
    127.      
    128. //然后电梯门关闭   
    129. $lift->close();   
    130.      
    131. class="comment">//再然后,电梯跑起来,向上或者向下   
    132. $lift->run();      
    133.  //最后到达目的地,电梯挺下来   
    134. $lift->stop();  

     

    显然我们已经完成了我们的基本业务操作,但是,我们在程序中使用了大量的switch…case这样的判断(if…else也是一样),首先是程序的可阅读性很差,其次扩展非常不方便。一旦我们有新的状态加入的话,例如新加通电和断点状态。我们势必要在每个业务方法里边增加相应的case语句。也就是四个函数open,close,run,stop都需要修改相应case语句。

    状态模式:把不同状态的操作分散到不同的状态对象里去完成。看看状态类的uml类图:

    图片 2

    代码实现:

     

    [php] view plain data-mod="popu_168"> data-mod="popu_168"> copy

     

    data-mod="popu_169"> print?

    1. <?php  
    2. /** 
    3.  *  
    4.  * 定义一个电梯的接口  
    5.  */   
    6. abstract  class="keyword">class LiftState{  
    7.   
    8.      class="comment">//定义一个环境角色,也就是封装状态的变换引起的功能变化  
    9.     protected   class="vars">$_context;  
    10.   
    11.     public  class="keyword">function setContext(Context  class="vars">$context){  
    12.         $this->_context =  class="vars">$context;  
    13.     }  
    14.   
    15.     //首先电梯门开启动作  
    16.     public  class="keyword">abstract  class="keyword">function open();  
    17.   
    18.      class="comment">//电梯门有开启,那当然也就有关闭了  
    19.     public  class="keyword">abstract  class="keyword">function close();  
    20.   
    21.     //电梯要能上能下,跑起来  
    22.     public  class="keyword">abstract  class="keyword">function run();  
    23.   
    24.      class="comment">//电梯还要能停下来,停不下来那就扯淡了  
    25.     public  class="keyword">abstract  class="keyword">function stop();  
    26.   
    27. }  
    28.   
    29.   
    30. /** 
    31. class="comment"> * 环境类:定义客户感兴趣的接口。维护一个ConcreteState子类的实例,这个实例定义当前状态。 
    32.  */   
    33. class Context {  
    34.     //定义出所有的电梯状态  
    35.     static   class="vars">$openningState = null;  
    36.     static   class="vars">$closeingState = null;  
    37.     static   class="vars">$runningState  = null;  
    38.     static   class="vars">$stoppingState = null;  
    39.   
    40.     public  class="keyword">function __construct() {  
    41.         self::$openningState =  class="keyword">new OpenningState();  
    42.         self::$closeingState =  class="keyword">new ClosingState();  
    43.         self::$runningState =   class="keyword">new RunningState();  
    44.         self::$stoppingState =  class="keyword">new StoppingState();  
    45.   
    46.     }  
    47.   
    48.     //定一个当前电梯状态  
    49.     private   class="vars">$_liftState;  
    50.   
    51.     public  class="keyword">function getLiftState() {  
    52.         return  class="vars">$this->_liftState;  
    53.     }  
    54.   
    55.     public  class="keyword">function setLiftState( class="vars">$liftState) {  
    56.         $this->_liftState =  class="vars">$liftState;  
    57.          class="comment">//把当前的环境通知到各个实现类中  
    58.          class="vars">$this->_liftState->setContext( class="vars">$this);  
    59.     }  
    60.   
    61.   
    62.     public  class="keyword">function open(){  
    63.          class="vars">$this->_liftState->open();  
    64.     }  
    65.   
    66.     public  class="keyword">function close(){  
    67.          class="vars">$this->_liftState->close();  
    68.     }  
    69.   
    70.     public  class="keyword">function run(){  
    71.          class="vars">$this->_liftState->run();  
    72.     }  
    73.   
    74.     public  class="keyword">function stop(){  
    75.          class="vars">$this->_liftState->stop();  
    76.     }  
    77. }  
    78.   
    79. /** 
    80. class="comment"> * 在电梯门开启的状态下能做什么事情  
    81.  */   
    82. class OpenningState  class="keyword">extends LiftState {  
    83.   
    84.     /** 
    85. class="comment">     * 开启当然可以关闭了,我就想测试一下电梯门开关功能 
    86.      * 
    87.      */  
    88.     public  class="keyword">function close() {  
    89.         //状态修改  
    90.          class="vars">$this->_context->setLiftState(Context:: class="vars">$closeingState);  
    91.          class="comment">//动作委托为CloseState来执行  
    92.          class="vars">$this->_context->getLiftState()->close();  
    93.     }  
    94.   
    95.     //打开电梯门  
    96.     public  class="keyword">function open() {  
    97.         echo  class="string">'lift open...',  class="string">'<br/>';  
    98.     }  
    99.      class="comment">//门开着电梯就想跑,这电梯,吓死你!  
    100.     public  class="keyword">function run() {  
    101.         //do nothing;  
    102.     }  
    103.   
    104.     //开门还不停止?  
    105.     public  class="keyword">function stop() {  
    106.         //do nothing;  
    107.     }  
    108.   
    109. }  
    110.   
    111. /** 
    112. class="comment"> * 电梯门关闭以后,电梯可以做哪些事情  
    113.  */   
    114. class ClosingState  class="keyword">extends LiftState {  
    115.   
    116.      class="comment">//电梯门关闭,这是关闭状态要实现的动作  
    117.     public  class="keyword">function close() {  
    118.         echo  class="string">'lift close...',  class="string">'<br/>';  
    119.   
    120.     }  
    121.      class="comment">//电梯门关了再打开,逗你玩呢,那这个允许呀  
    122.     public  class="keyword">function open() {  
    123.          class="vars">$this->_context->setLiftState(Context:: class="vars">$openningState);   class="comment">//置为门敞状态  
    124.          class="vars">$this->_context->getLiftState()->open();  
    125.     }  
    126.   
    127.      class="comment">//电梯门关了就跑,这是再正常不过了  
    128.     public  class="keyword">function run() {  
    129.          class="vars">$this->_context->setLiftState(Context:: class="vars">$runningState);  class="comment">//设置为运行状态;  
    130.          class="vars">$this->_context->getLiftState()->run();  
    131.     }  
    132.   
    133.     //电梯门关着,我就不按楼层  
    134.       
    135.     public  class="keyword">function stop() {  
    136.          class="vars">$this->_context->setLiftState(Context:: class="vars">$stoppingState);   class="comment">//设置为停止状态;  
    137.          class="vars">$this->_context->getLiftState()->stop();  
    138.     }  
    139.   
    140. }  
    141.   
    142. /** 
    143.  * 电梯在运行状态下能做哪些动作  
    144.  */   
    145. class RunningState  class="keyword">extends LiftState {  
    146.   
    147.     //电梯门关闭?这是肯定了  
    148.     public  class="keyword">function close() {  
    149.         //do nothing  
    150.     }  
    151.   
    152.      class="comment">//运行的时候开电梯门?你疯了!电梯不会给你开的  
    153.     public  class="keyword">function open() {  
    154.         //do nothing  
    155.     }  
    156.   
    157.     //这是在运行状态下要实现的方法  
    158.     public  class="keyword">function run() {  
    159.         echo  class="string">'lift run...',  class="string">'<br/>';  
    160.     }  
    161.   
    162.      class="comment">//这个事绝对是合理的,光运行不停止还有谁敢做这个电梯?!估计只有上帝了  
    163.     public  class="keyword">function stop() {  
    164.          class="vars">$this->_context->setLiftState(Context:: class="vars">$stoppingState);  class="comment">//环境设置为停止状态;  
    165.          class="vars">$this->_context->getLiftState()->stop();  
    166.     }  
    167.   
    168. }  
    169.   
    170.   
    171.   
    172. /** 
    173.  * 在停止状态下能做什么事情  
    174.  */   
    175. class StoppingState  class="keyword">extends LiftState {  
    176.   
    177.      class="comment">//停止状态关门?电梯门本来就是关着的!  
    178.     public  class="keyword">function close() {  
    179.         //do nothing;  
    180.     }  
    181.   
    182.     //停止状态,开门,那是要的!  
    183.     public  class="keyword">function open() {  
    184.          class="vars">$this->_context->setLiftState(Context:: class="vars">$openningState);  
    185.          class="vars">$this->_context->getLiftState()->open();  
    186.     }  
    187.     //停止状态再跑起来,正常的很  
    188.     public  class="keyword">function run() {  
    189.          class="vars">$this->_context->setLiftState(Context:: class="vars">$runningState);  
    190.          class="vars">$this->_context->getLiftState()->run();  
    191.     }  
    192.      class="comment">//停止状态是怎么发生的呢?当然是停止方法执行了  
    193.     public  class="keyword">function stop() {  
    194.         echo  class="string">'lift stop...',  class="string">'<br/>';  
    195.     }  
    196.   
    197. }  
    198.   
    199. /** 
    200.  * 模拟电梯的动作  
    201.  */   
    202. class Client {  
    203.   
    204.     public  class="keyword">static  class="keyword">function main() {  
    205.         $context =  class="keyword">new Context();  
    206.         $context->setLiftState( class="keyword">new ClosingState());  
    207.   
    208.         $context->open();  
    209.         $context->close();  
    210.         $context->run();  
    211.         $context->stop();  
    212.     }  
    213. }  
    214. Client::main();   

    9.与其他相关模式

     

    1)职责链模式,
    职责链模式和状态模式都可以解决If分支语句过多,
    从定义来看,状态模式是一个对象的内在状态发生改变(一个对象,相对比较稳定,处理完一个对象下一个对象的处理一般都已确定),
    而职责链模式是多个对象之间的改变(多个对象之间的话,就会出现某个对象不存在的现在,就像我们举例的公司请假流程,经理可能不在公司情况),这也说明他们两个模式处理的情况不同。
    这两个设计模式最大的区别就是状态模式是让各个状态对象自己知道其下一个处理的对象是谁。
    而职责链模式中的各个对象并不指定其下一个处理的对象到底是谁,只有在客户端才设定。
    用我们通俗的编程语言来说,就是
    状态模式:
      相当于If else if else;
      设计路线:各个State类的内部实现(相当于If,else If内的条件)
      执行时通过State调用Context方法来执行。
    职责链模式:
      相当于Swich case
      设计路线:客户设定,每个子类(case)的参数是下一个子类(case)。
      使用时,向链的第一个子类的执行方法传递参数就可以。
    就像对设计模式的总结,有的人采用的是状态模式,从头到尾,提前一定定义好下一个处理的对象是谁,而我采用的是职责链模式,随时都有可能调整链的顺序。

    2) 策略模式:(
            状态模式和策略模式的实现方法非常类似,都是利用多态把一些操作分配到一组相关的简单的类中,因此很多人认为这两种模式实际上是相同的。
    然而在现实世界中,策略(如促销一种商品的策略)和状态(如同一个按钮来控制一个电梯的状态,又如手机界面中一个按钮来控制手机)是两种完全不同的思想。当我们对状态和策略进行建模时,这种差异会导致完全不同的问题。例如,对状态进行建模时,状态迁移是一个核心内容;然而,在选择策略时,迁移与此毫无关系。另外,策略模式允许一个客户选择或提供一种策略,而这种思想在状态模式中完全没有。
           一个策略是一个计划或方案,通过执行这个计划或方案,我们可以在给定的输入条件下达到一个特定的目标。策略是一组方案,他们可以相互替换;选择一个策略,获得策略的输出。策略模式用于随不同外部环境采取不同行为的场合。我们可以参考微软企业库底层Object Builder的创建对象的strategy实现方式。而状态模式不同,对一个状态特别重要的对象,通过状态机来建模一个对象的状态;状态模式处理的核心问题是状态的迁移,因为在对象存在很多状态情况下,对各个business flow,各个状态之间跳转和迁移过程都是及其复杂的。
           例如一个工作流,审批一个文件,存在新建、提交、已修改、HR部门审批中、老板审批中、HR审批失败、老板审批失败等状态,涉及多个角色交互,涉及很多事件,这种情况下用状态模式(状态机)来建模更加合适;把各个状态和相应的实现步骤封装成一组简单的继承自一个接口或抽象类的类,通过另外的一个Context来操作他们之间的自动状态变换,通过event来自动实现各个状态之间的跳转。在整个生命周期中存在一个状态的迁移曲线,这个迁移曲线对客户是透明的。我们可以参考微软最新的WWF 状态机工作流实现思想。
          在状态模式中,状态的变迁是由对象的内部条件决定,外界只需关心其接口,不必关心其状态对象的创建和转化;
    而策略模式里,采取何种策略由外部条件(C)决定。
          他们应用场景(目的)却不一样,State模式重在强调对象内部状态的变化改变对象的行为,Strategy模式重在外部对策略的选择,策略的选择由外部条件决定,
    也就是说算法的动态的切换。但由于它们的结构是如此的相似,我们可以认为“状态模式是完全封装且自修改的策略模式”。即状态模式是封装对象内部的状态的,而策略模式是封装算法族的

     

    10.总结与分析

     

           状态模式的主要优点在于封装了转换规则,并枚举可能的状态,它将所有与某个状态有关的行为放到一个类中,并且可以方便地增加新的状态,只需要改变对象状态即可改变对象的行为,还可以让多个环境对象共享一个状态对象,从而减少系统中对象的个数;其缺点在于使用状态模式会增加系统类和对象的个数,且状态模式的结构与实现都较为复杂,如果使用不当将导致程序结构和代码的混乱,对于可以切换状态的状态模式不满足“开闭原则”的要求。

    本文由澳门新葡4473网站发布于新葡亰平台娱乐,转载请注明出处:设计模式 ( 十七) 状态模式State(对象行为型)

    关键词:

上一篇:ASCII码转化为UNICODE

下一篇:没有了