Iterator jest jednym z najczęściej wykorzystywanych szablonów w C#. Nawet nie zdając sobie z tego sprawy - korzystasz z niego na co dzień. Pozwala on na sekwencyjny dostęp do danych zawartych w kontenerze (lista, stos, kolejka itp.). Dzięki niemu zachowujemy enkapsulację oraz możemy w łatwy sposób zaimplementować dostęp do elementów od początku/końca struktury danych, przeskok o żądaną ilość obiektów itp.
Ogólny schemat Iteratora przedstawia poniższy schemat UML:
A więc zaczynając od lewej strony Aggregate tworzy interfejs tworzenia iteratora. ConcreteAggregate implementuje metodę tworzącą żądany iterator. Iterator tworzy interfejs umożliwiający przeglądanie elementów kontenera. ConcreteIterator - implementuje metody interfejsu oraz przechowuje informacje o aktualnej pozycji w przeglądanym kontenerze.
A więc przejdźmy do jakiegoś konkretnego przykładu. Wyobraźmy sobie że tworzymy jakiś kontener do przechowywania danych (SimpleList). Chcemy teraz w prosty sposób przeglądać nasze elementy. Dzięki implementacji iteratora możemy w łatwy sposób osiągnąć zamierzony cel:
public class ListNode
{
private int _data;
public int Data
{
get { return _data; }
set { _data = value; }
}
private ListNode _next;
public ListNode Next
{
get { return _next; }
set { _next = value; }
}
public ListNode(int data, ListNode next)
{
_data = data;
_next = next;
}
public ListNode(int data)
: this(data, null)
{
}
public ListNode()
: this(0, null)
{
}
}
public class SimpleList
{
private ListNode _head;
public SimpleList()
{
_head = null;
}
public IIterator CreateIterator()
{
return new SimpleListIterator(_head);
}
public void AddToFront(int date)
{
ListNode node = new ListNode(date);
if (_head == null)
{
_head = node;
}
else
{
node.Next = _head;
_head = node;
}
}
public void AddToEnd(int data)
{
ListNode node = new ListNode(data);
ListNode tmp = _head;
if (_head == null)
{
_head = node;
}
else
{
while (tmp.Next != null)
{
tmp = tmp.Next;
}
tmp.Next = node;
}
}
}
class Program
{
static void Main(string[] args)
{
SimpleList simpleList = new SimpleList();
simpleList.AddToFront(5);
simpleList.AddToFront(6);
simpleList.AddToFront(7);
simpleList.AddToEnd(20);
simpleList.AddToFront(30);
simpleList.AddToEnd(25);
IIterator simpleListIterator = simpleList.CreateIterator();
while (simpleListIterator.HasNext())
{
ListNode ln = (ListNode)simpleListIterator.Next();
Console.WriteLine(ln.Data);
}
Console.ReadLine();
}
}
public class SimpleListIterator : IIterator
{
ListNode listNode;
ListNode position;
public SimpleListIterator(ListNode head)
{
listNode = head;
position = head;
}
public object Next()
{
ListNode tmp = new ListNode();
tmp.Data = position.Data;
tmp.Next = position.Next;
position = position.Next;
return tmp;
}
public bool HasNext()
{
if (position != null)
{
return true;
}
return false;
}
public object First()
{
return listNode;
}
}
public interface IIterator
{
object Next();
bool HasNext();
object First();
}
C# domyślnie ma wbudowany iterator w kolekcjach jak i zwykłych tablicach. C# odpowiednik iteratora nazywany jest enumeratorem. Wszystkie klasy implementujące interfejs IEnumerable pozwalają na korzystanie z przechodzenia w sposób jawny i niejawny kolekcji:
static void Main(string[] args)
{
List<int> list = new List<int>();
Random rand = new Random();
for (int i = 0; i < 5; i++)
{
list.Add(rand.Next(100));
}
//jawny sposób
List<int>.Enumerator iterator = list.GetEnumerator();
while (iterator.MoveNext())
{
Console.WriteLine(iterator.Current);
}
Console.WriteLine();
//niejawny
foreach (var item in list)
{
Console.WriteLine(item);
}
}
Ze sposobu niejawnego korzystaliśmy z pewnością nieraz. Spróbujmy więc zaimplementować enumerator i generyczność do klasy SimpleList:
public class SimpleList<T> : IEnumerable<T>
{
private ListNode<T> _head;
public SimpleList()
{
_head = null;
}
public void AddToFront(T date)
{
ListNode<T> node = new ListNode<T>(date);
if (_head == null)
{
_head = node;
}
else
{
node.Next = _head;
_head = node;
}
}
public void AddToEnd(T data)
{
ListNode<T> node = new ListNode<T>(data);
ListNode<T> tmp = _head;
if (_head == null)
{
_head = node;
}
else
{
while (tmp.Next != null)
{
tmp = tmp.Next;
}
tmp.Next = node;
}
}
public IEnumerator<T> GetEnumerator()
{
ListNode<T> tmp = _head;
while (tmp != null)
{
T data = tmp.Data;
tmp = tmp.Next;
yield return data;
}
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
class Program
{
static void Main(string[] args)
{
SimpleList<int> simpleList = new SimpleList<int>();
simpleList.AddToFront(5);
simpleList.AddToFront(6);
simpleList.AddToFront(7);
simpleList.AddToEnd(20);
simpleList.AddToFront(30);
simpleList.AddToEnd(25);
foreach (var item in simpleList)
{
Console.WriteLine(item);
}
}
}
Jak wiadać ilość kodu się zmniejszyła a żądana funkcjonalność została.
Iterator to ważny wzorzec projektowy i warto go znać i stosować.
Brak komentarzy:
Prześlij komentarz