C# 设计模式系列教程-状态模式

作者:Wang Juqiang 时间:2022-11-07 13:31:55 

1. 概述

当一个对象的内在状态改变时允许改变其行为,这个对象看起来像是改变了其类。

2. 解决的问题

主要解决的是当控制一个对象状态转换的条件表达式过于复杂时的情况。把状态的判断逻辑转移到表示不同的一系列类当中,可以把复杂的逻辑判断简单化。

3. 模式中的角色

3.1 上下文环境(Context):它定义了客户程序需要的接口并维护一个具体状态角色的实例,将与状态相关的操作委托给当前的Concrete State对象来处理。

3.2 抽象状态(State):定义一个接口以封装使用上下文环境的的一个特定状态相关的行为。

3.3 具体状态(Concrete State):实现抽象状态定义的接口。

4. 模式解读

4.1 状态模式的类图

C# 设计模式系列教程-状态模式

4.2 状态模式的代码实现


/// <summary>
/// Context类,维护一个ConcreteState子类的实例,这个实例定义当前的状态。
/// </summary>
public class Context
{
 private State state;
 /// <summary>
 /// 定义Context的初始状态
 /// </summary>
 /// <param name="state"></param>
 public Context(State state)
 {
  this.state = state;
 }

/// <summary>
 /// 可读写的状态属性,用于读取和设置新状态
 /// </summary>
 public State State
 {
  get { return state; }
  set { state = value; }
 }

/// <summary>
 /// 对请求做处理,并设置下一个状态
 /// </summary>
 public void Request()
 {
  state.Handle(this);
 }
}

/// <summary>
/// 抽象状态类,定义一个接口以封装与Context的一个特定状态相关的行为
/// </summary>
public abstract class State
{
 public abstract void Handle(Context context);
}

/// <summary>
/// 具体状态类,每一个子类实现一个与Context的一个状态相关的行为
/// </summary>
public class ConcreteStateA : State
{
 /// <summary>
 /// 设置ConcreteStateA的下一个状态是ConcreteStateB
 /// </summary>
 /// <param name="context"></param>
 public override void Handle(Context context)
 {
  Console.WriteLine("当前状态是 A.");
  context.State = new ConcreteStateB();
 }
}

public class ConcreteStateB : State
{
 /// <summary>
 /// 设置ConcreteStateB的下一个状态是ConcreteSateA
 /// </summary>
 /// <param name="context"></param>
 public override void Handle(Context context)
 {
  Console.WriteLine("当前状态是 B.");
  context.State = new ConcreteStateA();
 }
}

4.3 客户端调用


class Program
{
 static void Main(string[] args)
 {
  // 设置Context的初始状态为ConcreteStateA
  Context context = new Context(new ConcreteStateA());

// 不断地进行请求,同时更改状态
  context.Request();
  context.Request();
  context.Request();
  context.Request();

Console.Read();
 }
}

运行结果

C# 设计模式系列教程-状态模式

5. 模式总结

5.1 优点

5.1.1 状态模式将与特定状态相关的行为局部化,并且将不同状态的行为分割开来。

5.1.2 所有状态相关的代码都存在于某个ConcereteState中,所以通过定义新的子类很容易地增加新的状态和转换。

5.1.3 状态模式通过把各种状态转移逻辑分不到State的子类之间,来减少相互间的依赖。

5.2 缺点

5.2.1 导致较多的ConcreteState子类

5.3 适用场景

5.3.1 当一个对象的行为取决于它的状态,并且它必须在运行时刻根据状态改变它的行为时,就可以考虑使用状态模式来。

5.3.2 一个操作中含有庞大的分支结构,并且这些分支决定于对象的状态。

6. 应用举例:电灯有两个状态,开(亮)与关(不亮),下面就用状态模式来实现对电灯的控制。

6.1 类图

C# 设计模式系列教程-状态模式

6.2 实现代码


/// <summary>
/// 电灯类,对应模式中的Context类
/// </summary>
public class Light
{
 private LightState state;

public Light(LightState state)
 {
  this.state = state;
 }

/// <summary>
 /// 按下电灯开关
 /// </summary>
 public void PressSwich()
 {
  state.PressSwich(this);
 }

public LightState State
 {
  get { return state; }
  set { state = value; }
 }
}

/// <summary>
/// 抽象的电灯状态类,相当于State类
/// </summary>
public abstract class LightState
{
 public abstract void PressSwich(Light light);
}

/// <summary>
/// 具体状态类, 开
/// </summary>
public class On : LightState
{
 /// <summary>
 /// 在开状态下,按下开关则切换到关的状态。
 /// </summary>
 /// <param name="light"></param>
 public override void PressSwich(Light light)
 {
  Console.WriteLine("Turn off the light.");

light.State = new Off();
 }
}

/// <summary>
/// 具体状态类,关
/// </summary>
public class Off: LightState
{
 /// <summary>
 /// 在关状态下,按下开关则打开电灯。
 /// </summary>
 /// <param name="light"></param>
 public override void PressSwich(Light light)
 {
  Console.WriteLine("Turn on the light.");

light.State = new On();
 }
}

6.3 客户端代码



class Program
{
 static void Main(string[] args)
 {
  // 初始化电灯,原始状态为关
  Light light = new Light(new Off());

// 第一次按下开关,打开电灯
  light.PressSwich();
  // 第二次按下开关,关闭电灯
  light.PressSwich();

Console.Read();
 }
}

执行结果

C# 设计模式系列教程-状态模式

标签:C#,设计模式,状态模式
0
投稿

猜你喜欢

  • C++动态数组类的封装实例

    2022-07-03 14:52:25
  • Android PhoneWindowManager监听屏幕右侧向左滑动实现返回功能

    2023-02-09 17:50:46
  • SpringBoot配置Profile实现多环境支持

    2023-07-29 21:53:20
  • 微服务间调用Retrofit在Spring Cloud Alibaba中的使用

    2022-09-29 23:13:42
  • java实现PDF转图片的方法

    2021-05-24 04:29:59
  • Android多媒体之画画板开发案例分享

    2022-11-17 11:05:08
  • Java校验银行卡是否正确的核心代码

    2022-01-28 19:53:29
  • Spring AOP实现打印HTTP接口出入参日志

    2021-10-09 13:38:37
  • 深入理解java重载和重写

    2022-09-28 12:19:32
  • Java编程实现中英混合字符串数组按首字母排序的方法

    2022-03-16 02:34:54
  • jdk15的安装与配置全过程记录

    2023-01-06 05:45:10
  • 使用Netty实现类似Dubbo的远程接口调用的实现方法

    2022-10-08 02:21:53
  • 解析Android游戏中获取电话状态进行游戏暂停或继续的解决方法

    2023-09-09 00:31:09
  • IDEA 2020 本土化,真的是全中文了(真香)

    2023-11-25 08:02:58
  • 简单介绍java中equals以及==的用法

    2023-01-28 07:47:41
  • 详解Spring boot使用Redis集群替换mybatis二级缓存

    2023-03-09 17:17:38
  • 详解Java如何创建Annotation

    2023-05-05 22:40:41
  • c语言重要的字符串与内存函数

    2023-04-28 00:35:42
  • ContentProvider启动流程示例解析

    2023-07-31 03:57:34
  • C#位运算以及实例计算详解

    2021-06-03 09:03:20
  • asp之家 软件编程 m.aspxhome.com