设计模式:浅谈行为模式State(C/C++ C#)

题外话:
    最近好忙啊,工作上还好说,主要是买了房子正在往房子里面塞东西,后悔房子买大了,要买这么多东西才能填满。

    今天重温了设计模式中的State,觉得有点心得想说说,通过C语言,C++和C#的几个例子,简单说说这种设计模式的应用和实际项目中的一些扩展,可能看到这大家都会说设计模式是面向对象的,C语言不是面向对象的,不是面向对象的语言就不可以用设计模式么,看看C语言的例子我们讨论一下。

    先看一下C#中的实现,这个例子是根据《设计模式》中给出的类设计视图的一个例子。
 1using System;
 2
 3namespace State
 4{
 5    //Abstract class of State
 6    public abstract class State
 7    {
 8        //Has only one Handle for presentation
 9        public abstract void Handle();
10    }

11
12    //ConcreteClass of State
13    public class ConcreteStateA : State
14    {
15        public override void Handle()
16        {
17            Console.WriteLine("A.Handle");
18        }

19    }

20
21    //Another ConcreteClass of State
22    public class ConcreteStateB : State
23    {
24        public override void Handle()
25        {
26            Console.WriteLine("B.Handle");
27        }

28    }

29
30    //Main logic Class
31    public class Context
32    {
33        State state;
34
35        //Constructor of Context it initiate state to be State A
36        public Context()
37        {
38            state = new ConcreteStateA();
39        }

40
41        //Control of state
42        public void ChangeState()
43        {
44            state = new ConcreteStateB();
45        }

46
47        //Behavior of Object
48        public void Request()
49        {
50            state.Handle();
51        }

52    }

53
54    //Application Class 
55    public class App
56    {
57        public static void Main()
58        {
59            Context context = new Context();
60            context.Request();
61            context.ChangeState();
62            context.Request();
63        }

64    }

65}
运行的结果为:
A.Handle
B.Handle

分析:通过这个例子我们可以看出,我们将两个具体的类,通过状态的管理进行了解耦,而对于状态的管理或者说控制呢,我们放在了Context这个类中来进行,对于调用的应用程序,我们只需要维护一个state对象,通过调用Request()方法来实现调用,通过ChangeState()这个方法来改变context的状态,实现同样的的Request()方法调用不同的状态方法。

这是一个比较简单的例子,可以说其实是最简单的实现例子,看起来像对抽象,再看一个同样简单的C++的例子。
#include "stdafx.h"

#include 
<iostream>
#include 
<string>

using namespace std;

class State
{
public:
    
virtual string response() = 0;
}
;

class Frog : public State
{
public:
    
string response()
    
{
        
return "Ribbet!!";
    }
;
}
;

class Prince : public State
{
public:
    
string response()
    
{
        
return "Darling";
    }

}
;

class Creature
{
    State
* state;

public:
    Creature():state(
new Frog())
    
{}

    
void greet()
    
{
        cout 
<< state->response() << endl;
    }


    
void kiss()
    
{
        delete state;
        state 
= new Prince();
    }

}
;

int main()
{
    Creature creature;
    creature.greet();
    creature.kiss();
    creature.greet();
}
这个实现是《C++编程思想 第2卷》 里面找到的一个例子,实现的内容几乎和前面一个例子相同,我稍微对代码进行了小的修改,可以看得出来,这是一个挺有意思的小例子,可以说是YY得很好,Kiss()之后青蛙变成了王子。看到这里我想大家应该对于state这个设计模式有了初步的了解,但是这个设计模式在实际的工作中是如何使用的呢,在什么时候我们才需要使用这样的设计模式呢,还有刚才说的C语言的问题是怎么回事呢,我们继续讨论。

先分析一下这个设计模式的适用范围吧,个人觉得这个设计模式很好,解决了很多问题,实际应用中也是非常常用的一个设计模式,但是并不是没有缺点或者说应该注意适用性的地方
第一个缺点就是可能会造成冗余,我们看前两个例子可以看得出来,状态的基类中只有一个方法,通常这是不太现实的,现实的是,可能会有很多很多方法,因为这个设计模式需要我们在基类列出所有的方法,不管这个方法是所有状态都会使用的,还是只有某个特殊状态才会使用的,这个时候不需要使用特定的方法的状态可能会产生冗余,虽然我们可以用空函数或者抛出异常来解决这个问题。对于10个以内的方法我觉得还好,如果上升到更高的数字级别的时候,也就是说不同的状态对应过多的方法的时候,需要另外的处理方式。

第二个是注意点,继续第一个问题,其实我们还可以使用另外一种方法来扩展这个设计模式。
首先用一个例子来更方面的说明,比如我们现在要做的模块或者说类的实例需要从很多其他模块中收各种各样的消息。
消息一共有100条,对应不同类型的消息,我们需要在我们这个模块的不同状态之间跳转,而且是无序的,然后再根据当前的状态,调用不用消息应该对应的各自的方法。假设我们有20各状态,也就是说,我需要写20个这样的状态类,每个状态类中需要写100中Handle(),这样算下来要写20000个实现,代码看起来估计会是很丑陋的,但是我还真见过这样写的,呵呵,数量级比这还高,最可怕的是着软件还卖出去了,怕怕。废话少说,这种情况我们怎么办呢,其实做一个简单的扩展就可以了。

分析这个实例,其实这20各状态中冗余是很大的,可能每个状态真正需要实现代码的只是对于10各消息的对应。我们找出这些对应的关系,将对应同类型消息的状态划分出来,单独作为一个状态,进入这个状态的时候,我们再在这个状态之中建立一个状态机,再次进行状态的检索,找到真正需要实现的内容,这样虽然我们将状态进行了嵌套,但是可以很好地解决代码冗余的问题。

最后再说说C语言的实现,其实并不是说设计模式只能用在面向对象程序设计当中,个人觉得设计模式只是一种方法论,是个非常灵活的东西,在我经历的项目中很多都是使用了状态机这个概念的C语言开发,觉得使用得很好,还是说到前面提到的一个概念,适用性的问题,面对大量的状态迁移,每种状态又对应多种实现要求的时候,这种设计模式还是非常有效的,设置从架构体系上来说都是非常简洁的一种方法。简单看一个例子:
int32 (*Jump_table[][3])() =
{
    
{   /* Reading State */
        Function_Read,            
/* Read Request */
        Function_Null,            
/* Write Request */    
        Function_Null,            
/* Protect Request */

    }
,
    
{   /* Writing State */
        Function_Null,            
/* Read Request */
        Function_Write,            
/* Write Request */    
        Function_Null,            
/* Protect Request */
    }
,
    
{   /* Protecting State */
        Function_Null,            
/* Read Request */
        Function_Null,            
/* Write Request */    
        Function_Protect,s        
/* Protect Request */
    }

}

假设这是一个二维函数数组,使用State的设计思想,我们很用想到用各种状态对应各种不同的处理响应,其中Jump_table这个数组起到的作用就是管理这个函数数组的各个状态和对应的响应,剩下需要做的就是来实现各个函数的处理,和对这个函数数组做一个解析的工作,当不同的要求在不同的状态的时候正确找到应该响应的函数而已。

以上个人对于State设计模式的一点小见解,希望大家指点,呵呵,其实觉得23中设计模式并不是都是很常用的,不过这个State我可以说是天天在用啊,很方便的东西,嘿嘿。

posted on 2007-06-07 16:33  Big Angel  阅读(1836)  评论(2编辑  收藏  举报

导航