2

我正在尝试对某个过程进行建模,并且我认为状态模式可能是一个很好的匹配。我想得到你的反馈,但关于 State 是否适合我的需求以及它应该如何与我的持久性机制相结合。

我有一个包含许多对象的 CMS,例如 Pages。这些对象(我们将使用 Pages 的示例,但大多数对象都是如此)可以处于多种状态之一,3 个示例是: 未发布 已发布 返工

未发布时,它们是可编辑的。一旦发布,它们就不可编辑,但可以移动到 Reworking 状态。在 Reworking 状态下,它们可以再次编辑并且可以重新发布。

显然,这些页面是否可编辑的决定应该在模型本身而不是 UI 中。所以,状态模式突然出现在脑海中。但是,如何防止将值分配给对象的属性?检查每个属性设置器似乎是个坏主意:

if (!CurrentState.ReadOnly)

任何想法如何工作?有没有更好的模式呢?

4

2 回答 2

3

使用维基百科的Java 示例,该结构有一个上下文,它调用在基本状态中定义的方法,具体状态会覆盖这些方法。

状态图

在您的情况下,上下文类似于页面。在某些州,该edit()方法只是无操作。上下文中的某些操作可能会隐式执行状态更改。客户端代码中永远不需要测试您所处的状态。

于 2010-01-07T22:03:14.083 回答
1

更新:

实际上,我今天早上想到了一种方法,该方法适用于您的特定情况并且更容易维护。我将在此处保留最初的两点,但我将推荐最终选项,因此请跳至“更好的方法”部分。

  1. 创建一个ThrowIfReadOnly方法,按照它在锡上所说的去做。这稍微减少了重复性并避免了嵌套。

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

选项(2)或多或少是如何IList一起ReadOnlyCollection<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; }
    }
}

这不仅或多或少地实现了状态模式,而且您不需要为不同的状态维护单独的类甚至单独的代码路径。例如,如果您想将InvalidOperationException转换为无操作,只需从方法中删除该throw语句即可。Write或者,如果你想添加一个额外的状态,比如Reviewing或类似的东西,你只需要添加case一行。

这不会为您处理状态转换或根据状态执行不同操作的任何真正复杂的操作(不仅仅是“成功”或“失败”),但听起来您不需要那样做。因此,这为您提供了一个插入式状态实现,几乎不需要使用额外的代码。

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

于 2010-01-07T22:00:36.000 回答