2010-01-07 72 views
2

我想模拟一个特定的过程,我认为状态模式可能是一个很好的匹配。我希望得到你的反馈,但是关于国家能否满足我的需求以及它应该如何与我的持久性机制相结合。状态模式对只读状态有用吗?

我有一个包含多个对象的CMS,例如Pages。这些对象(我们将使用页面的例子,但它的大多数对象true)可以在多个状态之一,3个例子是: 未发布 发布 返工

当出版,它们是可编辑的。一旦发布,它们不可编辑,但可以移入重做状态。在Reworking状态下,它们可以再次编辑,并可以重新发布。

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

if (!CurrentState.ReadOnly) 

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

回答

1

更新:

其实我今天上午将与您的特定情况下工作,是一个更容易保持想到了一个方法。我将在这里留下原始的两点,但我会推荐最后的选项,所以请跳到“更好的方法”部分。

  1. 创建一个ThrowIfReadOnly方法,它在罐子上做什么。这个重复性稍低,避免了嵌套。

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

选项(2)的更多或更少的如何IListReadOnlyCollection<T>一起工作。这样可以节省您在每个方法开始时进行检查的麻烦(从而消除了忘记验证的风险),但需要您维护两个类。

- 更好的方法 -

一个适当的技术规范将有很大的帮助澄清这一问题。我们在这里真正拥有的是:

  • A 系列任意“写”动作;
  • 每个动作具有相同结果,依赖于国家:
  • 要么采取动作(未发表/再加工),或失败/(只读)空操作。

真的有什么需要抽象是没有这么多的行动本身,但执行说行动。因此,功能性善良的点点将帮助我们在这里:

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变为无操作,只需从Write方法中删除throw语句。或者,如果你想添加一个额外的状态,如Reviewing或类似的东西,你只需要添加一条case行。

这不会为你处理或任何真正复杂的动作是做取决于国家(不仅仅是“成功”或“失败”等)不同的事物状态转换,但它听起来并不像你需要的。所以这给你一个插入状态的实现,它几乎不需要额外的代码来使用。

当然,还是有依赖注入/ AOP的选择,但显然还有很多与该方法相关的开销,我可能不会用它的东西那么简单。

+0

谢谢,我在想选项2,它可以很好地工作 – hackerhasid 2010-01-07 22:24:04

+0

我想这就是我一直在寻找的答案。我非常喜欢这个语法,谢谢! – hackerhasid 2010-01-11 14:59:49

3

使用维基百科的Java example,该结构具有一个背景下,这就要求在基本状态定义的方法,其具体状态覆盖。

State Diagram

在你的情况下,上下文是类似的页面。在一些州,edit()方法只是一个没有操作。上下文中的某些操作可能会隐式执行状态更改。有没有在客户端代码中任何需要测试你是在状态。

+0

是的,但Pages有一组属性和其他对象,说Module,有其他对象。你将如何处理? – hackerhasid 2010-01-07 22:20:14

+0

我认为状态模式是处理工作流状态的好主意。这几乎是问题的范围。我认为超出这个范围的设计问题应该得到他们自己的问题。特别是,“模块”是需要定义的那些实用词之一。 – 2010-01-07 23:21:03

+0

+1 for *在客户端代码中永远不需要测试你所处的状态。* I.E.如果存在,模式没有正确实施。 – radarbob 2012-06-05 19:50:35