关于c#:如何持久化实现State模式的对象?

How to persist objects which implement the State pattern?

我不熟悉状态设计模式,找不到将对象的不同状态保存到数据库(在我的例子中是SQL Server)的正确示例。这个场景与下一篇文章中描述的例子非常相似[几乎相同],但是我还没有找到将状态持久化到数据库的适用解决方案。你们能推荐一个链接或者举个例子吗?

C语言中的状态模式用法和示例#

另外:如何在运行时枚举所有不同的具体状态类型?例如,如果您有10个不同的状态,您是声明一个有10个不同成员的枚举状态并给每个具体状态成员一个关联的枚举状态成员,还是通过获取具体状态的子类来获取所有不同的状态?

对于您的信息,我需要能够根据不同的状态搜索实体。


状态的实例本身没有状态,所以您需要保存的只是每个状态的标识。在数据库中保存状态类名不是一个好主意,因为如果更改了状态类名,数据库就必须更改。相反,

  • 为每个状态类提供一个具有该状态唯一枚举值的成员。
  • 当持久化具有状态的对象时,持久化枚举。

要在加载对象时恢复状态,可以

  • 立即实例化对象的状态成员并将其分配给对象的状态成员,或者
  • 如果实例化一个状态很昂贵,请更改对象以通过方法访问状态成员,然后根据状态标识枚举成员的值在该方法中延迟实例化状态。

不管怎样,您都需要能够从枚举值转到状态。通过循环遍历所有相关的状态类,直到找到标识值匹配的状态类。

那么,相关的状态是什么?这取决于谁在写州立课程。

  • 在简单的情况下,如果控制整个程序,并且程序中的所有状态类都可能是具有对象的状态的成员,那么您可以循环访问状态超类或接口的所有子类或实现者,比如:获取实现接口的所有类型。

  • 如果出于某种原因,您不想循环使用状态类,那么只需定义一个列表,其中列出了您希望循环使用常量的状态类,或者(如果您希望在不更改代码的情况下对其进行更改)配置中的状态类。

如果列状态类的速度很慢,只需在程序启动或首次使用时执行一次。如果对列表进行硬编码,那么不要在有类的状态(它应该独立于特定的状态)或状态超类(这将引入循环依赖关系)中执行此操作;将列表放在程序中更高的位置(依赖关系方面),或者(如Farhad建议的那样)放在自己的类中。

有很多例子说明了如何持久化有状态的对象;这个例子相对简单。


不要试图将状态转换为表中的列,这样做行不通。

相反,使用json.net序列化状态,因为它支持继承。然后把它放在一张桌子上,比如:

1
2
3
4
5
create table OrderStates
(
    OrderId int not null,
    Data nvarchar(MAX) not null
);

如果需要,请包含更多的列,但仅包含用于标识状态的列。

要激活json.net中的继承支持,必须使用:

1
2
3
4
5
6
7
8
var json = JsonConvert.SerializeObject(yourState, typeof(StateBaseClass), JsonConvert.DefaultSettings)`.
using (var cmd = sqlConnection.CreateCommand())
{
    cmd.CommandText ="INSERT INTO OrderStates (OrderId, Data) VALUES(@OrderId, @Data)";
    cmd.Parameters.AddWithValue("OrderId", orderId);
    cmd.Parameters.AddWithValue("Data", json);
    cmd.ExecuteNonQuery();
}

反序列化时也一样,使用JsonConvert.DeserializeObject()时指定基类。

How do you enumerate all different ConcreteState types at run time? For instance, if you have 10 different states, do you declare an EnumStates with 10 different members and give every single ConcreteState member an associated EnumStates member, or you do get all the distinct states by getting the subclasses of ConcreteState?

子类。这是在不需要修改其他类的情况下引入新状态或删除旧状态的唯一方法。对现有类的每次修改都会引入错误。


我不喜欢您链接的示例,下面列出了原因:

  • 我同意,在我看来,持续的架构将是一种混乱的方式。
  • 在我看来,为每个州创建新的实例就像高效自杀模式。
  • 测试将是地狱…发现错误将是地狱。调试将是地狱。
  • 在30多年的经验中,我从未见过这种模式在以数据为中心的应用程序中被使用过一次——我确实看到过它,并在我不需要保存信息的情况下使用过它,例如在构建网络层时——每个端口都可以用这种状态模式处理。
  • < BR>我会选择这个模式:

    模式基础设施

    1
    2
    3
    4
    5
    public interface IStateObject<T>
    {
        T State { get; set; }
        void Process();
    }

    一些伪顺序对象的示例实现

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    public enum OrderState
    {
        Taken,
        Approved,
        Payed,
        Emailed,
        BeforeShipment
        //etc.. etc..
     }

     public class Order : IStateObject<OrderStates>
     {
         //some linear fields of order..
         //: name, description, etc.. etc..

         public OrderStates State { get; set; }

         public void Process()
         {
             switch (State)
             {
                 case OrderState.Taken:
                     // code to handle this state
                     break;
                 case OrderState.Approved:
                     // etc..
                     break;
              }
             //persist myself to db.
         }
     }

    它非常简单,因为您可以在一行中按对象上下文的类型保存对象。< BR>同样,如果我们没有计算机靠近我们,那么一个对象会以直观的方式创建一次。
    但主要是因为它非常直和灵活。

    你可能会注意到你实际上根本不需要IStateObject,但我认为你以后需要它来处理垂直决策。记住,T不必是枚举。它可以作为共同的基础,根据您的应用程序的需求进行发展。

    为了进一步指出我在回答开头提到的混乱情况,
    比如说,我们希望有订单先前状态的历史记录:

    使用此答案中提供的模式-添加PreviousOrderState属性,现在每行都有历史记录。还有其他的方法我相信你能想到……

    但是使用"状态模式"——你将面临严重的问题……要做到这一点,实际上需要一个完整的"尺度层次"来复杂化。您必须能够从每种类型的表链接到每种类型的其他表,或者尝试强制数据库上的面向对象…

    明白我的意思了吗?状态模式不适用于以数据为中心的应用程序。

    祝你好运。


    我同意戴夫的回答,直到加载对象时需要获得状态为止。我不认为当你有许多不同的对象有状态时,总是通过所有的状态类或者甚至是一个类列表来进行测试是一个好主意。

    我认为在这种情况下,应该有一个StateManager类,它还可以包含状态枚举定义,以及每个Enum值与其State对象(Dictionary之间的映射。这个映射应该是硬编码的,或者从配置文件中读取。这个类可以处理首次访问状态时的延迟加载。如果状态实际上没有字段,而是功能(如OP提交的示例链接中所示),它还可以将它们创建为singleton对象。


    状态模式可用于以数据为中心的系统,如处理工作流订单和审批业务的Web应用程序。状态操作和持久存储发生在状态切换逻辑的不同阶段。例如,委托对象将负责状态切换,而数据库操作应在状态切换事件发生时进行。您还可以预先定义状态机对象中的所有业务状态流。当状态更改事件出现时,触发状态机以确定它是否在预定义的流中。一个小的演示可以在https://github.com/elimisteve/fsm中找到。