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".)