I thought that Iterator -- another GoF pattern -- might be a good example of something that encapsulates state switching logic in some cases, when the states are switched strictly in the order you know beforehand. I realize that not every State implementation is that simple, but if yours is, you might want to use the idea: just pass the IEnumerable of IState to the implementation of State, and use MoveNext() and Current {get;} on the Enumerator internally to switch states. If you are doing something like this, it might be a good idea to jump back to State #1 after you've reached State #Last. I've written a little helper class to do just that:
using System;
using System.Collections.Generic;
namespace Primitives
{
public class CycledEnumerator<T> : IEnumerator<T>
where T : class
{
private LinkedList<T> _chainedObjects;
private LinkedListNode<T> _currentObject;
public CycledEnumerator(IEnumerable<T> objectsToChain)
{
if (objectsToChain == null)
{
throw new ArgumentNullException("objectsToChain");
}
_chainedObjects = new LinkedList<T>(objectsToChain);
if (_chainedObjects.Contains(null))
{
throw new ArgumentException("Nulls are not allowed.", "objectsToChain");
}
if (_chainedObjects.Count < 1)
{
throw new ArgumentException("Need at least one item in the set.", "objectsToChain");
}
this.MoveBeforeFirstElement();
}
private void MoveBeforeFirstElement()
{
_currentObject = null; // I don't like nulls at all, but I'm putting it here for the sake of consistency with Microsoft's implementations of Enumerator (which is a variant of GoF Iterator pattern).
}
public T Current
{
get
{
return _currentObject.Value;
}
}
public void Dispose()
{
}
object System.Collections.IEnumerator.Current
{
get
{
return _currentObject;
}
}
public bool MoveNext()
{
if ((_currentObject == null) || (_currentObject.Next == null))
{
MoveToFirstElement();
}
else
{
_currentObject = _currentObject.Next;
}
return true;
}
private void MoveToFirstElement()
{
_currentObject = _chainedObjects.First;
}
public void Reset()
{
this.MoveBeforeFirstElement();
}
}
}
(That said, I don't like Iterator much (and all of its buddies, like the Enumerator): it exposes too much of its state to the client, and has too complex a contract. It's all like "if MoveNext() returns true, then Current contains the next element", or: "after initialization the Enumerator is positioned before the first element, and you should call MoveNext() to get anything from it".)