C# 中你应该掌握的状态设计模式

作者:微信公众号:【架构师老卢】
7-4 13:12
47

概述:c#中的状态设计模式#使用 C# 中的状态设计模式,您可以允许对象在其内部状态更改时更改其行为。在本文中,我们将;从类图到真实世界的代码示例中探索状态设计模式,揭示其优势,剖析缺点,确保它符合 SOLID 原则。到最后,您将成为状态设计模式的大师,巧妙地增强您的软件。状态设计模式在软件开发中具有重要意义,特别是在增强代码的灵活性和可维护性方面。此设计模式允许对象在其内部状态更改时更改其行为,将每个状态视为一个单独的类。通过将每个状态封装在不同的类中,对象可以委托特定于状态的功能,从而可以更轻松地添加新状态或修改现有状态,而无需更改对象的结构。此外,状态模式通过集中与每个状态相关的逻辑来促进更清

c#中的状态设计模式#

使用 C# 中的状态设计模式,您可以允许对象在其内部状态更改时更改其行为。

在本文中,我们将;

  • 从类图到真实世界的代码示例中探索状态设计模式
  • 揭示其优势,
  • 剖析缺点,
  • 确保它符合 SOLID 原则。

到最后,您将成为状态设计模式的大师,巧妙地增强您的软件。

状态设计模式在软件开发中具有重要意义,特别是在增强代码的灵活性和可维护性方面。此设计模式允许对象在其内部状态更改时更改其行为,将每个状态视为一个单独的类。通过将每个状态封装在不同的类中,对象可以委托特定于状态的功能,从而可以更轻松地添加新状态或修改现有状态,而无需更改对象的结构。

此外,状态模式通过集中与每个状态相关的逻辑来促进更清晰、更模块化的代码。这不仅简化了对代码库的理解,而且便于将来添加新状态。此外,State 模式支持开放/封闭原则,使开发人员能够在不修改现有代码的情况下扩展系统的行为。

过渡到具体的例子,考虑在卫星连接(例如卫星因特网链接)中应用状态模式。正如该模式管理网络连接的各种状态一样,它也有助于无缝管理卫星系统的各种状态,从而改进软件的整体设计和维护。

同样,设想在恒温器或智能手机等日常设备中应用状态模式。通过采用这种设计方法,这些设备可以有效地管理不同的状态,并适当响应用户输入。例如,在恒温器的情况下,可以采用状态模式来处理各种温度设置,从而实现更具动态性和适应性的系统。

从本质上讲,状态设计模式提供了一种结构化且可扩展的方式来根据对象的内部状态管理对象的行为,从而有助于提高软件系统的整体稳健性和可扩展性。

状态设计模式的类图

您将在下面找到类图。

状态设计模式的类图

元素

  • **电视类:**该类充当电视的各种状态的上下文或容器。它维护对当前状态对象的引用,并将特定于状态的行为委托给该对象。此类封装了与电视的不同状态相关的功能,从而允许更加模块化和可维护的代码结构。
  • **IPossibleStates 接口:**该接口定义了一组方法,这些方法表示与电视的不同状态关联的可能行为或操作。每个具体的状态类都实现此接口,从而确保所有状态类中的方法集一致。此接口充当一个协定,该协定在每个具体状态类中强制实现特定于状态的行为。
  • **具体类(静音、开和关):**具体类(如 、 和 )、实现接口并表示电视的特定状态。每个类都封装了与其各自状态关联的行为。例如,该类定义电视在静音状态时的行为方式,而 and 类分别处理电视打开或关闭时的行为。这些类有助于清晰地分离关注点,从而可以轻松添加或修改状态,而不会影响类的整体结构。

解决方案资源管理器

您将找到该程序各部分的高级结构。

State Design Pattern 示例项目的解决方案资源管理器Solution explorer of the State Design Pattern sample project

实现

在管理电视状态的上下文中,实现涉及上述实体。

因此,让我们为示例项目的元素创建代码。

电视类。

public class Tv
 {
     private IPossibleStates _currentState;
     public IPossibleStates CurrentState
     {

         get
         {
             return _currentState;
         }

         set
         {
             _currentState = value;
         }
     }
     public Tv()
     {
         _currentState = new Off(this);
     }
     public void PressOffButton()
     {
         _currentState.PressOffButton(this);//Delegating the state
     }
     public void PressOnButton()
     {
         _currentState.PressOnButton(this);//Delegating the state
     }
     public void PressMuteButton()
     {
         _currentState.PressMuteButton(this);//Delegating the state
     }
 }

IPossibleStates 接口。

 public interface IPossibleStates
 {
     void PressOnButton(Tv context);
     void PressOffButton(Tv context);
     void PressMuteButton(Tv context);
 }

On.cs类实现 IPossibleStates 接口。

public class On: IPossibleStates
  {
      Tv _tvContext;
      public On(Tv context)
      {
          Console.WriteLine("Tv is On now.");
          _tvContext = context;
      }

      public void PressOnButton(Tv context)
      {
          Console.WriteLine("On button is pressed. Tv is already in On state.");
      }

      public void PressOffButton(Tv context)
      {
          Console.WriteLine("Off button is pressed. Going from On to Off state.");

          _tvContext.CurrentState = new Off(context);
      }

      public void PressMuteButton(Tv context)
      {
          Console.WriteLine("Mute button is pressed. Going from On to Mute mode.");
          _tvContext.CurrentState = new Mute(context);
      }
  }

Off.cs类实现 IPossibleStates 接口。

   public class Off: IPossibleStates
   {
       Tv _tvContext;
       //Initially we'll start from Off state
       public Off(Tv context)
       {
           Console.WriteLine("Tv is Off now.");
           _tvContext = context;
       }
       //Users can press any of these buttons at this state-On, Off or Mute
       //Tv is Off now, user is pressing On button
       public void PressOnButton(Tv context)
       {
           Console.WriteLine("On button is pressed. Going from Off to On state");

           _tvContext.CurrentState = new On(context);
       }
       //Tv is Off already, user is pressing Off button again
       public void PressOffButton(Tv context)
       {
           Console.WriteLine("Off button is pressed. Tv is already in Off state");
       }

       public void PressMuteButton(Tv context)
       {
           Console.WriteLine("Mute button is pressed. Tv is already in Off state, so Mute operation will not work.");
       }
   }

Mute.cs类实现 IPossibleStates 接口。

   internal class Mute: IPossibleStates
  {
      Tv _tvContext;
      public Mute(Tv context)
      {
          Console.WriteLine("Tv is in Mute mode now.");
          _tvContext = context;
      }
      //Users can press any of these buttons at this state-On, Off or Mute
      //Tv is in mute, user is pressing On button
      public void PressOnButton(Tv context)
      {
          Console.WriteLine("On button is pressed. Going from Mute mode to On state.");

          _tvContext.CurrentState = new On(context);
      }
      //Tv is in mute, user is pressing Off button
      public void PressOffButton(Tv context)
      {
          Console.WriteLine("Off button is pressed. Going to Mute mode to Off state.");

          _tvContext.CurrentState = new Off(context);
      }
      //Tv is in mute already, user is pressing mute button again
      public void PressMuteButton(Tv context)
      {
          Console.WriteLine(" Mute button is pressed. Tv is already in Mute mode.");
      }
  }

因此,我们将它们放在program.cs类中。

Console.WriteLine("***State Pattern Demo***\n");
//Initially Tv is Off
Tv Tv = new Tv();
Console.WriteLine("User is pressing buttons in the following sequence: ");

Console.WriteLine("Off->Mute->On->On->Mute->Mute->Off\n");
//Tv is already in Off state
Tv.PressOffButton();
//Tv is already in Off state, still pressing the Mute button
Tv.PressMuteButton();
//Making the Tv on
Tv.PressOnButton();
//Tv is already in On state, pressing On button again
Tv.PressOnButton();
//Putting the Tv in Mute mode
Tv.PressMuteButton();
//Tv is already in Mute, pressing Mute button again
Tv.PressMuteButton();
//Making the Tv off
Tv.PressOffButton();
// Wait for user
Console.Read();

您将在下面找到输出。

State Design Pattern 示例项目的输出

在此 C# 实现中,IPossibleStates 接口定义了常用方法 HandleOperation,该方法由 Mute.cs、On.cs 和 Off.cs 类实现。

Tv.cs类管理当前状态,并通过 IPossibleStates 接口将操作委托给具体的状态对象。

客户端代码演示了如何通过 Tv.cs 类动态控制状态转换,展示了状态设计模式在管理电视状态的上下文中的灵活性。

状态设计模式的优点

**模块化和清晰度:**通过将每个状态表示为单独的类,状态模式促进了模块化。每个状态类都封装了其特定行为,使代码更清晰、更易于维护。在不影响整个系统的情况下,更容易理解和修改各个状态的行为。

**简化的过渡逻辑:**状态转换封装在状态类中,从而简化了转换逻辑。上下文将特定于状态的操作委托给当前状态,当发生转换时,上下文只是切换到不同的状态对象,从而产生更清晰、更有序的代码。

**灵活性和可扩展性:**状态模式允许对象动态更改其行为,从而提供灵活性。在运行时在不同状态之间切换的能力使系统更能适应不断变化的需求。它还有助于添加新状态和修改现有状态,从而提供可扩展性。

**改进的测试:**使用 State 模式,测试变得更加简单。每个状态类都可以独立测试,确保与特定状态关联的行为是正确的。这种模块化方法增强了整个系统的可靠性。

**降低条件复杂度:**如果没有状态模式,管理对象响应各种状态的行为通常涉及复杂的条件语句。状态模式消除了对大量条件的需求,从而使代码更清晰、更易读,并降低了出错的可能性。

**促进利益分离:**该模式通过隔离每个状态在其自身类别中的行为来促进关注点的分离。这种分离增强了代码组织,并有助于识别和解决与特定状态相关的问题,而不会影响其他状态。

状态设计模式的缺点

**增加班级数量:**实现 State 模式通常会导致更多的类,尤其是当存在许多状态并且每个状态都需要自己的类时。类的这种激增可能会使代码库更加复杂且难以管理。

**过多子类的可能性:**如果应用程序中的状态数量众多,则该模式可能会导致大量子类,每个子类代表一个特定状态。管理大型状态类层次结构可能会变得笨拙,并影响代码的可读性。

**情境感知:**上下文(其行为随不同状态而变化的对象)需要了解所有可能的状态。这种意识可能会使上下文类变得更加复杂,开发人员必须确保它可以无缝处理过渡而不会引入错误。

**紧密耦合的潜力:**在某些情况下,上下文和状态类之间可能存在紧密耦合。状态类的接口变化可能需要对上下文类进行相应的修改,这违反了松耦合原则。

**理解状态转换的复杂性:**如果状态转换很复杂并且涉及多个条件,则状态模式可能不是最直观的解决方案。理解过渡的复杂性可能会增加,尤其是在过渡没有详细记录的情况下。

**对象创建开销:**创建多个状态对象可能会带来开销,尤其是在频繁创建状态的情况下。在需要优化对象创建的情况下,这可能会影响性能。

C 语言中的状态设计模式和 SOLID 原则#

**单一责任原则 (SRP):**状态设计模式提供了一种结构,其中每个状态都由一个单独的类表示。这确保了每个状态在设计中都有自己的责任和行为。例如,“处理中”、“已发货”、“已交付”等订单的状态表示为不同的类,每个类都管理自己的功能。这意味着每个州都有单一的责任,并且更改仅影响相关的州类别,从而遵守 SRP。

**开/闭原理 (OCP):**状态设计模式有助于在不修改现有状态的情况下添加新状态。要添加新状态,只需创建一个新的状态类,无需更改现有状态或其他组件。这允许在不更改现有代码的情况下使用新行为扩展系统,符合 OCP 的原则。

**Liskov 替代原理 (LSP):**状态设计模式允许每个状态从接口或抽象类扩展。这意味着任何状态都可以在代码库的任何地方使用,只要它实现接口或扩展抽象类。例如,表示订单的任何状态的所有类都继承自同一接口或抽象类。这确保了状态的可互换性,并支持 LSP。

**接口隔离原则 (ISP):**状态设计模式定义了小型和特定的接口或抽象类,每个接口或抽象类仅表示与特定状态相关的行为。这使对象只能与封装在状态类中的必要行为进行交互。例如,要更改订单的状态,只需与相关的状态类交互,该状态类包含该状态所需的最小功能。这与 ISP 一致。

**依赖反转原则 (DIP):**状态设计模式依赖于抽象接口或类来更改对象的状态。这将依赖性指向抽象,从而最大程度地减少对具体状态的依赖性。例如,订单的状态通过状态接口进行管理,并使用实现此接口的具体状态类。这提高了代码的灵活性和更易于维护,并遵守 DIP。

结论

当我们结束对 C# 中状态设计模式的探索时,很明显,它是软件开发库中一个多功能且有价值的工具。

源代码获取:公众号回复消息【code:51231

相关代码下载地址
重要提示!:取消关注公众号后将无法再启用回复功能,不支持解封!
第一步:微信扫码关键公众号“架构师老卢”
第二步:在公众号聊天框发送code:51231,如:code:51231 获取下载地址
第三步:恭喜你,快去下载你想要的资源吧
阅读排行