Menu
CoddyTech

状态模式

本课属于 Coddy C# 学习旅程的“面向对象编程”单元。第 60 课,共 70 课。

状态模式是一种行为型模式,它允许对象在内部状态改变时改变其行为。你可以将每种状态封装在自己的类中,并将行为委托给当前状态对象,而不是使用复杂的条件语句来处理不同的状态。

该模式由三个部分组成:定义特定状态行为的状态接口、实现该行为的具体状态类,以及保存当前状态引用的上下文类:

public interface IDocumentState
{
    void Publish(Document doc);
}

public class DraftState : IDocumentState
{
    public void Publish(Document doc)
    {
        Console.WriteLine("Moving to moderation");
        doc.SetState(new ModerationState());
    }
}

public class ModerationState : IDocumentState
{
    public void Publish(Document doc)
    {
        Console.WriteLine("Publishing document");
        doc.SetState(new PublishedState());
    }
}

public class PublishedState : IDocumentState
{
    public void Publish(Document doc)
    {
        Console.WriteLine("Already published");
    }
}

上下文保存当前状态,并将操作委托给该状态。状态对象可以通过调用上下文的 SetState() 来触发状态转换:

public class Document
{
    private IDocumentState _state = new DraftState();
    
    public void SetState(IDocumentState state) => _state = state;
    
    public void Publish() => _state.Publish(this);
}

var doc = new Document();
doc.Publish();  // 进入审核阶段
doc.Publish();  // 正在发布文档
doc.Publish();  // 已经发布

状态模式消除了用于检查当前状态的冗长 if-else 或 switch 语句。每个状态类只处理自己的行为,让代码更易于维护,也更容易扩展新状态。

challenge icon

挑战

简单

让我们使用状态模式构建一个交通灯系统。交通灯会在不同状态(红色、黄色、绿色)之间循环,每种状态都会决定灯切换时会发生什么。你将把每种灯的状态封装在单独的类中,而不是使用复杂的条件语句。

你将把代码组织在 3 个文件中:

  • TrafficLightState.cs:在 Traffic 命名空间中定义 ITrafficLightState 接口,并包含一个方法 Change(TrafficLight light)。然后创建 3 个具体的状态类:
    • RedState:调用 Change 时,输出 Red - Stop! Changing to Green...,并将灯切换到 GreenState
    • GreenState:输出 Green - Go! Changing to Yellow...,并切换到 YellowState
    • YellowState:输出 Yellow - Caution! Changing to Red...,并切换到 RedState
  • TrafficLight.cs:在同一命名空间中创建一个 TrafficLight 类。它是你的上下文类,用于维护当前状态。它默认从 RedState 开始,并包含:
    • 一个 SetState(ITrafficLightState state) 方法,供状态对象触发状态切换
    • 一个 Change() 方法,将调用委托给当前状态的 Change 方法
  • Program.cs:创建一个 TrafficLight,根据输入循环切换状态,将所有内容整合起来。

你会收到一个输入:

  • 交通灯切换的次数(一个整数)

创建一个 TrafficLight(它从红灯状态开始),并按指定次数调用 Change()。

例如,如果输入是 3,输出应为:

Red - Stop! Changing to Green...
Green - Go! Changing to Yellow...
Yellow - Caution! Changing to Red...

如果输入是 5,灯会循环回到起始状态:

Red - Stop! Changing to Green...
Green - Go! Changing to Yellow...
Yellow - Caution! Changing to Red...
Red - Stop! Changing to Green...
Green - Go! Changing to Yellow...

注意,TrafficLight 类不包含关于每种颜色代表什么或接下来会切换到哪种颜色的逻辑,所有这些信息都保存在各个状态类中。每种状态都负责自己的行为,并触发切换到下一个状态!

亲手试试

using System;
using Traffic;

class Program
{
    public static void Main(String[] args)
    {
        // 读取交通信号灯需要切换的次数
        int n = Convert.ToInt32(Console.ReadLine());

        // TODO:创建一个 TrafficLight 实例(默认从 RedState 开始)

        // TODO:使用循环调用 Change() 方法 n 次
    }
}
quiz icon自我测试

这节课包含一个小测验。开始学习即可作答,并记录你的进度。

面向对象编程中的所有课

自己练习: C# 在线编译器