状态模式:解耦MoveNext()方法



在以下StackOverflow文章的帮助下,我尝试了实现状态模式:

状态模式

到目前为止还不错。我现在可以将对象(文档)移动到下一个状态,即ACKNOWLEDGED。)

public override void MoveNext(Document currDoc, IProcessor currProcessor)
{                               
TransitionTo<ACKNOWLEDGED>(() => new ACKNOWLEDGED(_factory, _context));
currProcessor.LogTheChange(currDoc);  
currProcessor.DoSomethingElse(currDoc)
}

现在,在更改状态的过程中,我想在不同的类中执行其他操作。我怀疑将状态和IProcessor耦合是否是个好主意。

我认为状态应该只关注在两个谨慎状态之间的变化(在这种情况下是UNACKNOWLEDGED和ACKNOWLED)。

底层对象的实际更新是如何发生的?据我所见,我已经将doc对象传递给了moveNext()方法,这样它就可以被传递到其他地方。如果这是不正确的,那么我的状态对象应该如何以解耦的方式与IProcessor通信,它应该引发IProcessor将处理的事件吗?或者,我应该在MoveNext()方法中将接口作为参数传递吗?我怀疑不是。

嗯。一个有趣的问题,我会这么说。如果状态转换只有一种效果(即让IProcessor做一些事情或做一些事情),那么这种方法可能就很好了

即使状态更改可能会导致许多事情发生,但MoveNext()函数是引发此更改的唯一方法,那么仅向该方法添加更多处理器和操作可能并不可怕。如果某个处理器抛出异常,您可能最终不得不担心会发生什么。

然而,如果状态更改可以从许多地方开始(许多函数如MoveNext(),或者如果状态可以根据条件自行更改),那么您将希望拥有监视状态更改的实体。您可以使用某种发布和订阅机制来使用它,或者简单地按照约定,当状态实体发出消息时,某些对象会进行监视。

如果是我,我可能会把我的状态对象连接到类似spring.net事件系统的东西上(http://springframework.net/doc-latest/reference/html/quickstarts.html)。然而,在.net中可能还有其他不涉及spring的方法可以做到这一点。

我认为在内部使用State模式的类的调用方不应该知道状态机的存在。因此,我不喜欢调用Documemt.MoveNext的客户端代码。它暴露了许多实现细节。

这里有一个替代实现,它将状态模式隐藏在Document类中。注意,我使用私有内部类来完全隐藏状态机的详细信息,同时从每个状态子类中提供对Document成员的完全访问。不过,我仍然将这些内部类保存在它们自己的代码文件中,以避免Document类文件中的代码混乱。

文件.cs

partial class Document
{
public Document()
{
// default/starting state
this.TransitionToState<EmptyState>();
}
// misc data for example
public int? caseNumber { get; private set;}
public DateTime? WhenSubmitted { get; private set; }
public DateTime? WhenAcknowlegded { get; private set; }
public int? CompletionStatus { get; private set; }
// transitions:  EMPTY -> ASSIGNED -> UNACKNOWLEDGED -> ACKNOWLEDGED -> COMPLETED
private DocumentState State { get; set; }

// state-related methods are forwarded to the current DocumentState instance
public void AssignCase(int caseNumber)
{
State.AssignCase(caseNumber);
}
public void SubmitTo(object clientInfo)
{
State.SubmitTo(clientInfo);
}
public void Acknowledged(object ackInfo)
{
State.Acknowledged(ackInfo);
}
public void Complete(int statusCode)        
{
State.Complete(statusCode);
}
// events could be used for this callback as well, but using private inner 
//  classes calling a private member is probably the simplest.
private void TransitionToState<T>() where T : DocumentState, new()
{
// save prior for a moment
DocumentState priorState = State;
// this can be lookup from map instead of new() if you need to keep them 
//  alive for some reason.  I personally like flyweight states.
DocumentState nextState = new T();
// activate the new state.  it will get notified so it can do any one-
//  time setup
State = nextState;
State.EnterState(this);
// let the prior state know as well, so it can cleanup if needed
if (priorState != null)
priorState.ExitState();
}        
}

DocumentState.cs

partial class Document
{
abstract class DocumentState
{
//--------------------------------------------
// state machine infrastructure 
//--------------------------------------------
public void EnterState(Document context)
{
this.Context = context;
Console.WriteLine("Entering state: " + this.GetType().Name); // debug only
OnEnterState();
}
public void ExitState()
{
this.Context = null;
OnExitState();
Console.WriteLine("State that was exited: " + this.GetType().Name); // debug only
}
protected Document Context { get; private set; }
//--------------------------------------------
// a mirror of the document-manipulation methods that concerns states
//--------------------------------------------
public void AssignCase(int caseNumber)
{
OnAssignCase(caseNumber);
}
public void SubmitTo(object clientInfo)
{
OnSubmitTo(clientInfo);
}
public void Acknowledged(object ackInfo)
{
OnAcknowledged(ackInfo);
}
public void Complete(int statusCode)
{
OnComplete(statusCode);
}
//--------------------------------------------
// state subclasses override the methods they need.  Typically not 
//  all are needed by all states.  Default implementation is to
//  throw an exception if a state receives and "unexpected" invocation.
//--------------------------------------------
protected virtual void OnAssignCase(int caseNumber)
{
throw new InvalidOperationException();
}
protected virtual void OnSubmitTo(object clientInfo)
{
throw new InvalidOperationException();
}
protected virtual void OnAcknowledged(object ackInfo)
{
throw new InvalidOperationException();
}
protected virtual void OnComplete(int statusCode)
{
throw new InvalidOperationException();
}
//--------------------------------------------
// additional hooks that can be override if needed that signal the
//  enter and exit of the state.
//--------------------------------------------
protected virtual void OnEnterState()
{
}
protected virtual void OnExitState()
{
}
}
}

状态类(为了便于说明,我添加了其他类):

partial class Document
{
// Represents an empty document waiting to get assigned a case #.  Once 
//  that is satisfied, it performs its logic and triggers a state 
//  transition to the next state.
class EmptyState : DocumentState
{
protected override void OnAssignCase(int caseNumber)
{
// business logic
Context.caseNumber = caseNumber;
// write to log
// etc, etc
// goto next state
Context.TransitionToState<AssignedState>();
}
}
}
partial class Document
{
// Represents an document assigned a ase number but not submitted to a
//  client yet.  Once that happens it performs its logic and the triggers a state 
//  transition.
class AssignedState : DocumentState
{
protected override void OnSubmitTo(object clientInfo)
{
// business logic
Context.WhenSubmitted = DateTime.Now;
// etc
// etc
// goto next state
Context.TransitionToState<UnacknowledgedState>();
}
}
}
partial class Document
{        
// you get the idea by now...
class UnacknowledgedState : DocumentState
{
protected override void OnAcknowledged(object ackInfo)
{
// business logic
Context.WhenAcknowlegded = DateTime.Now;
// goto next state
Context.TransitionToState<AcknowledgedState>();
}
}
}
partial class Document
{
class AcknowledgedState : DocumentState
{
protected override void OnComplete(int statusCode)
{
Context.CompletionStatus = statusCode;
Context.TransitionToState<CompletedState>();
}
}
}
partial class Document
{
class CompletedState : DocumentState
{
// note there are no methods overriden.  this is the last state.
}
}

最后,Program.cs:

class Program
{
static void Main(string[] args)
{
Console.ReadLine();
Document doc = new Document();
Console.ReadLine();
doc.AssignCase(123456);
Console.ReadLine();
doc.SubmitTo("clientAddress");
Console.ReadLine();
doc.Acknowledged("responseFromClient");
Console.ReadLine();
const int TERMS_REJECTED = 123;
doc.Complete(TERMS_REJECTED);
Console.ReadLine();
}
}

如果你有任何问题,请告诉我。

相关内容

最新更新