温故知新(2)——状态模式

概述

状态模式是一种比较常用的设计模式,属于GOF23种设计模式中行为模式的一种。

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

上面是《设计模式——可复用面向对象软件基础》一书中给出的这个模式的意图。通俗一点说就是一个对象具有多种状态,在每个状态下,对象的行为(代码逻辑)不同。对于简单的情况我们可以用if…else…或者swich语句来处理这中情况,不过当状态复杂多变,而且每个行为都因状态的不同而大不相同时,可以考虑引入状态模式来化解危机。

状态模式将每个状态和与其相关的行为封装的单独的类中,因此很容易添加新的状态;对象的状态很容易转换,可以避免在状态转换时出现不一致的情况;与状态相关的行为也可以独立的变化修改。但是状态模式引入了多个表示状态的小类,使的逻辑分散在不同的类中,不够紧凑,增加了代码阅读的难度。因此是否使用状态模式,还要权衡利弊得失而定。

结构

下面是状态模式的类图。

状态模式

状态模式的结构比较简单,下面列出模式的参与者:

1、对象(书中成为环境,通常理解为包含不同状态的那个对象),提供用户感兴趣的接口,并且包含一个状态接口的引用——Context

2、对象状态的一个状态接口,用以状态与状态有关的行为——IState

3、状态的具体实现——StateA、StateB

示例

下面通过一个示例来说明状态模式的实现。假设我们有一个这样的场景:客户在我们的系统中投放广告,一般的流程是,发布——展示——展示完成。在这期间客户可以对广告进行修改、取消广告(退回支付的广告费)、终止广告(也许出现了什么法律问题),但是在广告处于不同的状态下,这些操作是不同的,具体规则如下:

1、客户可以修改已发布状态的广告;

2、客户可以取消已发布状态的广告;

3、客户不能对展示中的广告进行修改和取消操作,只能进行终止操作;

4、系统将广告由已发布变为展示中,从展示中变为展示完成,不能从已发布直接变为展示完成;

5、客户取消和终止的广告变为取消状态,正常展示完成的广告变为展示完成状态;

6、取消和展示完成为广告的最终状态不可以在做任何操作。

广告业务用UML状态图表示如下:

image

为了示例简单,我省略了审核等一些流程,可以看出如果使用分支判断的方式来实现,将会非常繁杂,因此此处使用状态模式是比较合适的。

1、定义状态接口IAdState,这个接口包含了用户关系的五个行为。

 1:  using System;
 2:   
 3:  namespace DesignPatterns.State
 4:  {
 5:      /// <summary>
 6:      /// 广告状态接口
 7:      /// </summary>
 8:      public interface IAdState
 9:      {
10:          /// <summary>
11:          /// 修改方法
12:          /// </summary>
13:          IAdState Update();
14:   
15:          /// <summary>
16:          /// 退款(撤销)
17:          /// </summary>
18:          IAdState Refund();
19:   
20:          /// <summary>
21:          /// 取消(终止)
22:          /// </summary>
23:          IAdState Abort();
24:   
25:          /// <summary>
26:          /// 展示
27:          /// </summary>
28:          IAdState Display();
29:   
30:          /// <summary>
31:          /// 完成展示
32:          /// </summary>
33:          IAdState Complet();
34:      }
35:  }
36:   

2、具体实现状态。

PublishedState:

 1:  using System;
 2:   
 3:  namespace DesignPatterns.State
 4:  {
 5:      /// <summary>
 6:      /// 已发布
 7:      /// </summary>
 8:      public class PublishedState : IAdState
 9:      {
10:          public IAdState Update()
11:          {
12:              Console.WriteLine("未展示的广告允许修改");
13:              return this; //仍然是已发布状态。
14:          }
15:   
16:          public IAdState Refund()
17:          {
18:              Console.WriteLine("未展示的广告允许退款");
19:              return new CanceledState(); //变为取消状态
20:          }
21:   
22:          public IAdState Abort()
23:          {
24:              Console.WriteLine("未展示的广告允许取消,同时自动完成退款");
25:              return new CanceledState(); //变为取消状态
26:          }
27:   
28:          public IAdState Display()
29:          {
30:              Console.WriteLine("将未展示的广告变为展示中状态");
31:              return new DisplayingState(); //变为展示状态
32:          }
33:   
34:          public IAdState Complet()
35:          {
36:              Console.WriteLine("未展示的广告不允许直接变为展示完成状态");
37:              return this; //仍然是已发布状态。
38:          }
39:      }
40:  }
41:   
42:   

DisplayingState:

 1:  using System;
 2:   
 3:  namespace DesignPatterns.State
 4:  {
 5:      /// <summary>
 6:      /// 展示中
 7:      /// </summary>
 8:      public class DisplayingState : IAdState
 9:      {
10:          public IAdState Update()
11:          {
12:              Console.WriteLine("展示中的广告不允许修改");
13:              return this; //仍然是展示中状态。
14:          }
15:   
16:          public IAdState Refund()
17:          {
18:              Console.WriteLine("展示中的广告不允许退款");
19:              return this; //仍然是展示中状态。
20:          }
21:   
22:          public IAdState Abort()
23:          {
24:              Console.WriteLine("展示中的广告允许终止");
25:              return new CanceledState(); //变为取消状态。
26:          }
27:   
28:          public IAdState Display()
29:          {
30:              Console.WriteLine("广告已经处于展示中状态");
31:              return this; //仍然是展示中状态。
32:          }
33:   
34:          public IAdState Complet()
35:          {
36:              Console.WriteLine("广告展示完成");
37:              return new CompletedState(); //变为展示完成状态。
38:          }
39:      }
40:  }
41:   

CompletedState:

 1:  using System;
 2:   
 3:  namespace DesignPatterns.State
 4:  {
 5:      /// <summary>
 6:      /// 展示完成
 7:      /// </summary>
 8:      public class CompletedState : IAdState
 9:      {
10:          public IAdState Update()
11:          {
12:              Console.WriteLine("展示完成的广告不允许修改");
13:              return this; //仍然是展示完成状态。
14:          }
15:   
16:          public IAdState Refund()
17:          {
18:              Console.WriteLine("展示完成的广告不允许退款");
19:              return this; //仍然是展示完成状态。
20:          }
21:   
22:          public IAdState Abort()
23:          {
24:              Console.WriteLine("展示完成的广告不允许终止");
25:              return this; //仍然是展示完成状态。
26:          }
27:   
28:          public IAdState Display()
29:          {
30:              Console.WriteLine("展示完成的广告不允许展示");
31:              return this; //仍然是展示完成状态。
32:          }
33:   
34:          public IAdState Complet()
35:          {
36:              Console.WriteLine("已经处于展示完成状态");
37:              return this; //仍然是展示完成状态。
38:          }
39:      }
40:  }
41:   

CanceledState:

 1:  using System;
 2:   
 3:  namespace DesignPatterns.State
 4:  {
 5:      /// <summary>
 6:      /// 取消
 7:      /// </summary>
 8:      public class CanceledState : IAdState
 9:      {
10:          public IAdState Update()
11:          {
12:              Console.WriteLine("取消的广告不允许修改");
13:              return this; //仍然是取消状态。
14:          }
15:   
16:          public IAdState Refund()
17:          {
18:              Console.WriteLine("取消的广告不允许退款");
19:              return this; //仍然是取消状态。
20:          }
21:   
22:          public IAdState Abort()
23:          {
24:              Console.WriteLine("取消的广告不允许终止");
25:              return this; //仍然是取消状态。
26:          }
27:   
28:          public IAdState Display()
29:          {
30:              Console.WriteLine("取消的广告不允许展示");
31:              return this; //仍然是取消状态。
32:          }
33:   
34:          public IAdState Complet()
35:          {
36:              Console.WriteLine("取消的广告不允许变为完成展示状态");
37:              return this; //仍然是取消状态。
38:          }
39:      }
40:  }
41:   

3、包含状态的广告类Ad。

 1:  using System;
 2:   
 3:  namespace DesignPatterns.State
 4:  {
 5:      /// <summary>
 6:      /// 广告类
 7:      /// </summary>
 8:      public class Ad
 9:      {
10:          public Ad()
11:              : this(new PublishedState())
12:          { }
13:   
14:          public Ad(IAdState state)
15:          {
16:              this.state = state;
17:          }
18:   
19:          private IAdState state;
20:   
21:          public IAdState State
22:          {
23:              get { return state; }
24:          }
25:   
26:          /// <summary>
27:          /// 修改方法
28:          /// </summary>
29:          public void Update()
30:          {
31:              this.state = this.state.Update();
32:          }
33:   
34:          /// <summary>
35:          /// 退款(撤销)
36:          /// </summary>
37:          public void Refund()
38:          {
39:              this.state = this.state.Refund();
40:          }
41:   
42:          /// <summary>
43:          /// 取消(终止)
44:          /// </summary>
45:          public void Abort()
46:          {
47:              this.state = this.state.Abort();
48:          }
49:   
50:          /// <summary>
51:          /// 展示
52:          /// </summary>
53:          public void Display()
54:          {
55:              this.state = this.state.Display();
56:          }
57:   
58:          /// <summary>
59:          /// 完成展示
60:          /// </summary>
61:          public void Complet()
62:          {
63:              this.state = this.state.Complet();
64:          }
65:      }
66:  }
67:   

4、为了测试我们完成一些测试代码。

 1:  using System;
 2:   
 3:  namespace DesignPatterns.State
 4:  {
 5:      class Program
 6:      {
 7:          static void Main(string[] args)
 8:          {
 9:              Ad ad = new Ad();
10:              //对广告进行一些操作
11:              ad.Complet();
12:              ad.Update();
13:              ad.Display();
14:              ad.Update();
15:              ad.Complet();
16:              ad.Refund();
17:   
18:              Console.WriteLine("===按任意键结束...===");
19:              Console.ReadKey();
20:          }
21:      }
22:  }
23:   

5、查看运行结果。

image

在测试代码中任意对广告进行操作,可以查看到不同的操作结果。思考一下,当流程改变是如何进行修改。

posted @ 2012-09-02 16:43  宽厚  阅读(1032)  评论(0编辑  收藏  举报