如何持久化实现状态模式的对象?

时间:2014-05-21 10:23:03

标签: c# design-patterns state state-machine software-design

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

State Pattern Usage and Sample in C#

另外:如何在运行时枚举所有不同的ConcreteState类型?例如,如果你有10个不同的状态,你是否声明一个包含10个不同成员的EnumStates并为每个ConcreteState成员提供一个关联的EnumStates成员,或者你通过获取ConcreteState的子类来获得所有不同的状态?

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

5 个答案:

答案 0 :(得分:7)

国家实体本身没有国家,所以你需要保存的只是每个国家的身份。将State类名保存在数据库中并不是一个好主意,因为如果更改了State类名,则必须更改数据库。取而代之的是,

  • 为每个State类提供一个具有该状态唯一的Enum值的成员。
  • 当您持有具有State的对象时,请保留Enum。

要在加载对象时返回状态,

  • 立即实例化对象的State成员并将其分配给对象的State成员,或
  • 如果实例化一个State的代价很高,则通过一个方法更改对象以访问State成员,并根据State Identity Enum成员的值懒惰地实例化该方法中的State。

无论哪种方式,您都需要能够从Enum值转到State。通过循环遍历所有相关的State类来执行此操作,直到找到其标识值匹配的类。

那么,相关国家是什么?这取决于谁在写国家班。

  • 在简单的情况下,您控制整个程序并且程序中的所有State类都可能是State-having对象的成员,您可以遍历State超类的所有子类或实现者或界面,如下所示:Getting all types that implement an interface

  • 如果由于某种原因存在您不想循环的状态类,只需定义一个您想要在常量中循环的列表或(如果您想要更改它而没有在配置中更改代码。

如果使状态类列表很慢,只需在程序启动或首次使用时执行一次。如果您对列表进行硬编码,请不要在具有State的类中(它应该独立于特定的状态)或State超类(这会引入循环依赖);将列表放在你的程序中更高(依赖性)或(如Farhad建议的那样)在自己的类中。

There are a lot of examples how how to persist objects with State out there; this one相对简单。

答案 1 :(得分:4)

不要试图将状态转换为表中的列,这些列不起作用。

而是使用JSON.NET序列化状态,因为它支持继承。然后将其存储在如下表格中:

create table OrderStates
(
    OrderId int not null,
    Data nvarchar(MAX) not null
);

如果需要,请包含更多列,但只包含识别状态所用的列。

要激活JSON.NET中的继承支持,您必须使用:

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()时指定基类。

  

如何在运行时枚举所有不同的ConcreteState类型?例如,如果你有10个不同的状态,你是否声明一个包含10个不同成员的EnumStates并为每个ConcreteState成员提供一个关联的EnumStates成员,或者你通过获取ConcreteState的子类来获得所有不同的状态?

子类。这是能够引入新状态或删除旧状态而无需修改其他类的唯一方法。现有类的每次修改都可能引入错误。

答案 2 :(得分:3)

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

  1. 我同意这种坚持不懈的架构将是一团糟 - 在我看来是混乱的。
  2. 在每个州创建新实例在我看来都像是效率自杀模式。
  3. 测试将是地狱......发现错误将是地狱..调试将是地狱。
  4. 在30多年的经验中,我从未见过这种模式在数据中心应用程序中被使用过一次 - 我确实看到它并在我不需要保留信息的情况下使用它,例如在构建网络层时 - 每个端口都可以用这种状态模式处理。

  5. 我会选择这种模式:

    模式基础架构

    public interface IStateObject<T>
    {
        T State { get; set; }
        void Process();
    }
    

    某些伪订单对象的示例实现

    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.
         }
     }
    

    这非常简单,因为您可以在一行中按对象的每个上下文保存对象。
    如果我们没有计算机接近我们,也应该直观地创建一个对象。
    但主要是因为它非常直接且非常灵活。

    您可能会注意到实际上可能根本不需要IStateObject<T> - 但我认为您以后需要处理垂直决策时需要它。 请记住,T不一定是枚举。它可以作为根据您的应用需求发展的共同基础。

    进一步指出我在答案开头提到的混乱,
    假设我们希望将历史记录到订单的先前状态:

    使用此答案中提供的模式 - 您添加了PreviousOrderState属性,现在每行都有历史记录..还有其他方法我确定您可以想到..

    但是使用“状态模式” - 你将遇到严重的问题......它实际上会因为完整的“比例级别”而变得复杂......你必须能够从每种类型链接table对于每种类型的其他表 - 或者尝试在数据库上强制面向对象...

    看到我的观点? States模式根本不适用于以数据为中心的应用程序。

    祝你好运。

答案 3 :(得分:1)

我同意Dave的回答,直到你需要在加载对象时获得状态。我不认为当你有许多具有状态的不同对象时,总是遍历所有状态类甚至类列表都是个好主意。

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

答案 4 :(得分:0)

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

中找到一个小演示