状态模式对只读状态有用吗?

时间:2010-01-07 21:44:10

标签: c# design-patterns state

我正在尝试对某个过程建模,我认为状态模式可能是一个很好的匹配。我想了解一下State是否符合我的需求以及它应该如何与我的持久性机制相结合的反馈。

我有一个拥有众多对象的CMS,例如Pages。这些对象(我们将使用Pages的示例,但大多数对象都是如此)可以处于多种状态之一,3个示例是: 未公布 发布时间 再加工

当Unpublished时,它们是可编辑的。发布后,它们不可编辑,但可以进入返工状态。在Reworking状态下,它们可以再次编辑,可以重新发布。

显然,这些页面是否可编辑的决定应该在模型本身而不是UI中。因此,国家模式浮现在脑海中。但是,如何防止为对象的属性赋值?对每个属性设置器进行检查似乎是一个坏主意:

if (!CurrentState.ReadOnly)

任何想法如何工作?对此有更好的模式吗?

2 个答案:

答案 0 :(得分:3)

使用维基百科的Java example,结构有一个Context,它调用基本状态中定义的方法,具体状态覆盖这些方法。

State Diagram

在您的情况下,上下文就像一个页面。在某些州,edit()方法只是一种无操作方法。上下文中的一些动作可以隐式地执行状态改变。客户端代码中从不需要测试您所处的状态。

答案 1 :(得分:1)

<强>更新

我实际上想到了今天早上的方法,它可以适用于您的特定情况并且更容易维护。我将在这里留下原来的两点,但我将推荐最终选项,所以跳到“更好的方法”部分。

  1. 创建一个ThrowIfReadOnly方法,它可以完成它在锡上所说的内容。重复性稍差,避免嵌套。

  2. 使用界面。拥有实现所需功能的IPage,让每个公共方法返回IPage,然后有两个实现,EditablePageReadOnlyPage。只要有人试图修改它,ReadOnlyPage就会抛出异常。还在IsReadOnly接口上放置State属性(或IPage属性),以便消费者可以实际检查状态而无需捕获异常。

  3. 选项(2)或多或少是IListReadOnlyCollection<T>一起工作的方式。它为您省去了在每个方法开始时进行检查的麻烦(从而消除了忘记验证的风险),但需要您维护两个类。

    - 更好的方法 -

    适当的技术规范将有助于澄清这个问题。我们真正拥有的是:

    • 任意“写”动作的系列;
    • 每个操作都有相同的结果,具体取决于州:
    • 采取行动(未发布/重做)或失败/禁止行动(只读)。

    真正需要抽象的不是操作本身,而是所述操作的执行。因此,一点功能性的好处将帮助我们:

    public enum PublishingState
    {
        Unpublished,
        Published,
        Reworking
    }
    
    public delegate void Action();
    
    public class PublishingStateMachine
    {
        public PublishingState State { get; set; }
    
        public PublishingStateMachine(PublishingState initialState)
        {
            State = initialState;
        }
    
        public void Write(Action action)
        {
            switch (State)
            {
                case PublishingState.Unpublished:
                case PublishingState.Reworking:
                    action();
                    break;
                default:
                    throw new InvalidOperationException("The operation is invalid " +
                        "because the object is in a read-only state.");
            }
        }
    }
    

    现在自己编写类几乎变得微不足道了:

    public class Page
    {
        private PublishingStateMachine sm = new
            PublishingStateMachine(PublishingState.Unpublished);
    
        private string title;
        private string category;
    
        // Snip other methods/properties
        // ...
    
        public string Title
        {
            get { return title; }
            set { sm.Write(() => title = value; }
        }
    
        public string Category
        {
            get { return category; }
            set { sm.Write(() => category = value; }
        }
    
        public PublishingState State
        {
            get { return sm.State; }
            set { sm.State = value; }
        }
    }
    

    这不仅或多或少地实现了State模式,而且您不需要为不同的状态维护单独的类甚至单独的代码路径。例如,如果您想将InvalidOperationException变为无操作,只需从throw方法中删除Write语句即可。或者,如果您想添加其他状态,例如Reviewing或类似内容,则只需添加一条case行。

    这将不会处理您的状态转换或任何根据状态执行不同操作的复杂操作(除了“成功”或“失败”之外),但听起来并不像您需要的那样。因此,这为您提供了一个简单的状态实现,几乎不需要使用额外的代码。

    当然,仍然可以选择依赖注入/ AOP,但显然有很多与这种方法相关的开销,我可能不会将它用于这么简单的事情。