步步为营 .NET 设计模式学习笔记 八、State(状态模式)
概述
允许一个对象在其内部状态改变时改变它的行为。对象看起来似乎修改了它所属的类。
意图
状态模式主要解决的是当控制一个对象状态装换的条件表达式过于复杂时的情况。把状态的判断逻辑转移到表示不同状态的一系列类中,可以把复杂的判断逻辑简单化。
当一个对象行为取决于它的状态,并且它必须在运行时刻根据状态改变它的行为时,就可以考虑使用状态模式了。
<Design Pattern>State模式结构图
示例关系图
描述: 一年有12个月,有四个季度,每个月都有一属于一个季度,根据月份得到这个季度的天气信息.可以用State模式来实现.
关系图:
代码设计:
先创建接口IQuarter.cs:
01 |
public interface IQuarter |
02 |
{ |
03 |
/// <summary> |
04 |
/// 通过月份获取季度 |
05 |
/// </summary> |
06 |
/// <param name="Month"></param> |
07 |
/// <returns></returns> |
08 |
Quarter GetQuarter(); |
09 |
10 |
/// <summary> |
11 |
/// 气候条件 |
12 |
/// </summary> |
13 |
/// <returns></returns> |
14 |
string Climate(); |
15 |
} |
再创建Quarter.cs:
01 |
public abstract class Quarter:IQuarter |
02 |
{ |
03 |
private int _Month = 0; |
04 |
public int Month |
05 |
{ |
06 |
get |
07 |
{ |
08 |
return _Month; |
09 |
} |
10 |
set |
11 |
{ |
12 |
_Month = value; |
13 |
} |
14 |
} |
15 |
public Quarter( int month ) |
16 |
{ |
17 |
this .Month = month; |
18 |
} |
19 |
20 |
#region IQuarter 成员 |
21 |
22 |
public abstract Quarter GetQuarter(); |
23 |
24 |
public abstract string Climate(); |
25 |
26 |
#endregion |
27 |
28 |
|
29 |
} |
1 |
再创建Spring.cs: |
01 |
public class Spring : Quarter |
02 |
{ |
03 |
04 |
private Quarter quarter; |
05 |
public override string Climate() |
06 |
{ |
07 |
return "春风宜人,鸟语花香,正是旅游的好季节。" ; |
08 |
} |
09 |
10 |
public override Quarter GetQuarter() |
11 |
{ |
12 |
if (Month > 3) |
13 |
{ |
14 |
quarter = new Summer(Month); |
15 |
return quarter.GetQuarter(); |
16 |
} |
17 |
return new Spring(Month); |
18 |
} |
19 |
public Spring( int month): base (month) |
20 |
{ |
21 |
|
22 |
} |
23 |
} |
再创建Summer.cs:
01 |
public class Summer : Quarter |
02 |
{ |
03 |
private Quarter quarter; |
04 |
public override string Climate() |
05 |
{ |
06 |
return "夏季天气炎热,酷暑难熬." ; |
07 |
} |
08 |
public override Quarter GetQuarter() |
09 |
{ |
10 |
if (Month < 4) |
11 |
{ |
12 |
quarter = new Spring(Month); |
13 |
return quarter.GetQuarter(); |
14 |
} |
15 |
if (Month > 6) |
16 |
{ |
17 |
quarter = new Autumn(Month); |
18 |
return quarter.GetQuarter(); |
19 |
} |
20 |
21 |
return new Summer( this .Month); |
22 |
23 |
} |
24 |
public Summer( int month): base (month) |
25 |
{ |
26 |
|
27 |
} |
28 |
} |
再创建 Autumn.cs:
01 |
public class Autumn : Quarter |
02 |
{ |
03 |
private Quarter quarter; |
04 |
public override string Climate() |
05 |
{ |
06 |
return "秋高气爽." ; |
07 |
} |
08 |
public override Quarter GetQuarter() |
09 |
{ |
10 |
if (Month < 7) |
11 |
{ |
12 |
quarter = new Summer(Month); |
13 |
return quarter.GetQuarter(); |
14 |
} |
15 |
if (Month > 9) |
16 |
{ |
17 |
quarter = new Winter(Month); |
18 |
return quarter.GetQuarter(); |
19 |
} |
20 |
return new Autumn(Month); |
21 |
|
22 |
} |
23 |
public Autumn( int month): base (month) |
24 |
{ |
25 |
|
26 |
} |
27 |
28 |
} |
再创建Winter.cs:
01 |
public class Winter : Quarter |
02 |
{ |
03 |
private Quarter quarter; |
04 |
public override string Climate() |
05 |
{ |
06 |
return "冬天风寒,空气干燥." ; |
07 |
} |
08 |
public override Quarter GetQuarter() |
09 |
{ |
10 |
|
11 |
if (Month < 10) |
12 |
{ |
13 |
return quarter.GetQuarter(); |
14 |
quarter = new Autumn(Month); |
15 |
} |
16 |
return new Winter(Month); |
17 |
|
18 |
} |
19 |
public Winter( int month): base (month) |
20 |
{ |
21 |
|
22 |
} |
23 |
} |
再创建MonthInfo.cs:
01 |
public class MonthInfo |
02 |
{ |
03 |
private Quarter quarter; |
04 |
public MonthInfo( int month) |
05 |
{ |
06 |
//初始化为春季 |
07 |
quarter = new Spring(month); |
08 |
quarter = quarter.GetQuarter(); |
09 |
} |
10 |
public string ShowInfo() |
11 |
{ |
12 |
if (CheckMonthInfo()) |
13 |
{ |
14 |
return GetQuarterClimate(); |
15 |
} |
16 |
else |
17 |
{ |
18 |
return GetError(); |
19 |
} |
20 |
} |
21 |
private string GetQuarterClimate() |
22 |
{ |
23 |
return string .Format( "月份是:{0}月份,气候是:{1}" , this .quarter.Month.ToString(), this .quarter.Climate()); |
24 |
} |
25 |
private string GetError() |
26 |
{ |
27 |
return string .Format( "月份是:{0},是一个错误的信息." , this .quarter.Month.ToString()); |
28 |
} |
29 |
private bool CheckMonthInfo() |
30 |
{ |
31 |
if ( this .quarter.Month > 12 || this .quarter.Month < 1) |
32 |
{ |
33 |
return false ; |
34 |
} |
35 |
return true ; |
36 |
} |
37 |
} |
再调用它:
01 |
public partial class Run : Form |
02 |
{ |
03 |
public Run() |
04 |
{ |
05 |
InitializeComponent(); |
06 |
} |
07 |
08 |
private void btnRun_Click( object sender, EventArgs e) |
09 |
{ |
10 |
for ( int i = 0; i < 15; i++) |
11 |
{ |
12 |
MonthInfo monthInfo = new MonthInfo(i); |
13 |
rtbResult.AppendText(monthInfo.ShowInfo()+ "\n" ); |
14 |
} |
15 |
} |
16 |
} |
结果如图:
1 |
|
何时采用
1.一个对象的行为取决于它的状态,并且它必须在运行时刻根据状态改变它的行为。 2.一个操作中含有庞大的多分支的条件语句,且这些分支依赖于该对象的状态。这个状态通常用一个或多个枚举常量表示。通常,有多个操作包含这
一相同的条件结构。state模式将每一个条件分支放入一个独立的类中。这使得你可以根据对象自身的情况将对象的状态作为一个对象,这一对象可以不
依赖于其他对象而独立变化。
3.从代码角度来说,如果一个类有多种状态,并且在类内部通过的条件语句判断的类状态来实现不同行为时候可以把这些行为单独封装为状态类。
4.从应用角度来说,如果一个对象有多种状态,如果希望把对象状态的转化以及由不同状态产生的行为交给具体的状态类去做,那么可以考虑状态模式。
效果及实现要点
1.在环境角色中拥有状态角色的实例。
2.在状态角色中拥有环境角色的实例用于在具体状态中修改环境角色的状态。
3.状态对象之间的依赖可以通过加载外部配置的转化规则表等方法来消除。
4.状态模式和策略模式的主要区别是,前者的行为实现方式是由条件决定的,并且应当能不在客户端干预的情况下自己迁移到合适的状态,而后者的行为实现方式是由客户端选择的,并且能随时替换。
总结
1.优点: 避免了为判断状态而产生的巨大的if或case语句。 将对象行为交给状态类维护后,对于上层程序而言,仅需要维护状态之间的转换规则。
2.缺点:会导致某些系统有过多的具体状态类。
3.过多的状态对象可能会增加系统负担,可以考虑把各种状态角色实现为无状态对象的享元,需要保存的额外状态由环境角色进行统一管理和处理。