更新:
实际上,我今天早上想到了一种方法,该方法适用于您的特定情况并且更容易维护。我将在此处保留最初的两点,但我将推荐最终选项,因此请跳至“更好的方法”部分。
创建一个ThrowIfReadOnly
方法,按照它在锡上所说的去做。这稍微减少了重复性并避免了嵌套。
使用接口。有一个IPage
实现你想要的功能,让每个公共方法返回一个IPage
,然后有两个实现,一个EditablePage
和一个ReadOnlyPage
。ReadOnlyPage
只要有人试图修改它,就会抛出异常。还要在接口上放置一个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,但显然这种方法有很多开销,我可能不会将它用于这么简单的事情。