我已经从以下StackOverflow帖子中获得了一些帮助来实现状态模式:
到目前为止很好。 我现在可以移动对象(文档,到下一个状态,即ACKNOWLEDGED。)
public override void MoveNext(Document currDoc, IProcessor currProcessor)
{
TransitionTo<ACKNOWLEDGED>(() => new ACKNOWLEDGED(_factory, _context));
currProcessor.LogTheChange(currDoc);
currProcessor.DoSomethingElse(currDoc)
}
现在,在更改状态的过程中,我想在不同的类中执行其他操作。 我怀疑结合状态和IProcessor是个好主意。
我认为国家应该只关注两个谨慎状态之间的转换(在这种情况下是UNACKNOWLEDGED和ACKNOWLLEDGED)。
如何对底层对象进行实际更新?据我所见,我已将doc对象传递给moveNext()方法,只是为了可以将其传递到别处。如果这不正确,那我的状态对象如何以解耦的方式与IProcessor通信呢?它应该引发一个IProcessor将处理的事件吗?或者,我应该在MoveNext()方法中将接口作为参数传递吗?我怀疑不是。
答案 0 :(得分:0)
嗯。我会说这个有趣的问题。如果状态转换只有一个效果(使IProcessor做某事或某些事情),那么这种方法可能很好。
即使状态发生变化可能会发生很多事情,但MoveNext()函数是煽动此变化的唯一方法,那么为方法添加更多处理器和操作可能不会太可怕。如果您的某个处理器抛出异常,您最终可能会担心会发生什么。
但是,如果状态更改可以从许多地方启动(许多函数,如MoveNext(),或者状态可以根据条件自行更改),那么您将需要拥有监视状态更改的实体。您可以使用某种发布和订阅机制来使用它,或者只是按惯例使用,因为某些对象会监视您的州实体发出的消息。
如果是我,我可能会将我的状态对象挂钩到spring.net事件系统(http://springframework.net/doc-latest/reference/html/quickstarts.html)之类的东西。但是,在.net中可能有其他方法可以执行此操作,而不涉及spring。
答案 1 :(得分:0)
我不认为在内部使用State模式的类的调用者应该知道状态机存在。因此,我不是调用Documemt.MoveNext
的客户端代码的粉丝。它暴露了很多实现细节。
这是一个隐藏Document类中的状态模式的替代实现。请注意,我使用私有内部类来完全隐藏状态机的详细信息,同时提供对每个状态子类的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();
}
}
如果您有任何问题,请与我们联系。