Iterator
- Provides a way to access the elements of an object sequentially without exposing its underlying representation/data structure.
Classical Structure
Scenario
- Build a browser which store history
Problem
public class BrowseHistory
{
private List<string> urls = new List<string>();
public List<string> Urls { get => urls; }
public void Push(string url)
{
urls.Add(url);
}
public string Pop()
{
var lastIndex = urls.Count - 1;
var lastUrl = urls[lastIndex];
urls.RemoveAt(lastIndex);
return lastUrl;
}
}
- If the history class uses different data structure to store URLs, the main class will break.
- Every time we change the internals of the object, you end up with breaking changes
- Changing the internal of an object shouldn't affect it consumers
Solution
public interface IIterator
{
bool HasNext();
string Current();
void Next();
}
public class BrowseHistory
{
private List<string> urls = new List<string>();
public void Push(string url)
{
urls.Add(url);
}
public string Pop()
{
var lastIndex = urls.Count - 1;
var lastUrl = urls[lastIndex];
urls.RemoveAt(lastIndex);
return lastUrl;
}
public IIterator CreateIterator()
{
return new ListIterator(this);
}
public class ListIterator : IIterator
{
private readonly BrowseHistory history;
private int index;
public ListIterator(BrowseHistory history)
{
this.history = history;
}
public string Current()
{
return history.urls[index];
}
public bool HasNext()
{
return index < history.urls.Count;
}
public void Next()
{
index++;
}
}
}
public class Program
{
static void Main(string[] args)
{
var browseHistory = new BrowseHistory();
browseHistory.Push("a");
browseHistory.Push("b");
browseHistory.Push("c");
var iterator = browseHistory.CreateIterator();
while (iterator.HasNext())
{
var url = iterator.Current();
Console.WriteLine(url);
iterator.Next();
}
}
}
Example Structure
Example Code