设计模式(十七)—— 迭代器模式
模式简介
提供一种方法顺序访问一个聚合对象中的各个元素,而又不暴露该对象的内部表示。
一个聚合对象,应该提供一个方法让用户访问它的元素,并且不暴露其内部结构。遍历的方式可能多种多样,我们不希望在这个聚合类中定义各种遍历的方法。这种情况下,就可以使用迭代器模式将对列表的访问和遍历从列表对象中分离出来并放入一个迭代器对象中。
结构分析
UML类图
角色说明
- Aggregate
抽象聚合类,定义一个创建相应迭代器对象的接口。
- ConcreteAggregate
实际聚合类,实现创建相应迭代器对象的接口,返回一个ConcreteIterator实例。
- Iterator
抽象迭代器,定义访问和遍历元素的接口。
- ConcreteIterator
实际迭代器,实现抽象迭代器定义的接口。
工作原理
ConcreteIterator跟踪聚合中的当前对象,通过Next方法计算出待遍历的下一个对象。
结构代码
//抽象迭代器
abstract class Iterator
{
public abstract object First();
public abstract object Next();
public abstract bool IsDone();
public abstract object CurrentItem();
}
//抽象聚合类
abstract class Aggregate
{
public abstract Iterator CreateIterator();
}
//实际聚合类,实现Aggregate中的抽象方法CreateIterator,返回相应的迭代器
class ConcreteAggregate : Aggregate
{
private ArrayList _items = new ArrayList();
public override Iterator CreateIterator()
{
return new ConcreteIterator(this);
}
public object this[int index]
{
get { return _items[index]; }
set { _items.Insert(index, value); }
}
public int Count
{
get { return _items.Count; }
}
}
//实际迭代器
class ConcreteIterator : Iterator
{
private ConcreteAggregate _aggregate;
private int _current;
public ConcreteIterator(ConcreteAggregate aggregate)
{
_aggregate = aggregate;
}
public override object CurrentItem()
{
return _aggregate[_current];
}
public override object First()
{
return _aggregate[0];
}
public override bool IsDone()
{
return _current >= _aggregate.Count - 1;
}
public override object Next()
{
object next = null;
if (_current < _aggregate.Count - 1)
{
next = _aggregate[++_current];
}
return next;
}
}
//客户端调用
static void Main(string[] args)
{
ConcreteAggregate aggregate = new ConcreteAggregate();
aggregate[0] = "A";
aggregate[1] = "B";
aggregate[2] = "C";
aggregate[3] = "D";
var iterator = aggregate.CreateIterator();
Console.WriteLine(iterator.First());
while (!iterator.IsDone())
{
Console.WriteLine(iterator.Next());
}
Console.ReadLine();
}
示例分析
本节我们通过一个遍历音乐播放列表的示例来讲述迭代器模式。首先创建一个歌曲类,包含两个属性:歌曲名称和演唱者。
class Song
{
public string Name { get; set; }
public string Singer { get; set; }
}
定义迭代器接口以及聚合接口。
interface Iterator
{
object First();
object Next();
bool IsDone();
object CurrentItem();
}
interface IAggregate
{
Iterator CreateIterator();
}
创建歌曲迭代器,包含一个歌曲列表对象,并提供对这个列表中元素访问的相关方法。
class SongIterator : Iterator
{
private SongCollection _songCollection;
private int _current;
public SongIterator(SongCollection songCollection)
{
this._songCollection = songCollection;
}
public object First()
{
return _songCollection[0];
}
public object Next()
{
object song = null;
if (_current < _songCollection.Count - 1)
{
song = _songCollection[++_current];
}
return song;
}
public bool IsDone()
{
return _current >= _songCollection.Count - 1;
}
public object CurrentItem()
{
return _songCollection[_current];
}
}
歌曲聚合类,提供一个创建针对该聚合的迭代器的方法。
class SongCollection : IAggregate
{
private ArrayList _songs = new ArrayList();
public Iterator CreateIterator()
{
return new SongIterator(this);
}
public object this[int index]
{
get { return _songs[index]; }
set { _songs.Insert(index, value); }
}
public int Count
{
get { return _songs.Count; }
}
}
客户端调用:
class Program
{
static void Main(string[] args)
{
SongCollection songs = new SongCollection();
songs[0] = new Song { Name = "Dance To This", Singer = "Troye Sivan" };
songs[1] = new Song { Name = "Ferrari", Singer = "Bebe Rexha" };
songs[2] = new Song { Name = "Ocean", Singer = "Martin Garrix" };
songs[3] = new Song { Name = "Sober", Singer = "Demi Lovato" };
songs[4] = new Song { Name = "Only You", Singer = "Cheat Codes" };
Iterator iterator = songs.CreateIterator();
var firstSong = iterator.First() as Song;
Console.WriteLine($"First Song:{firstSong.Name},Singer:{firstSong.Singer}");
while (!iterator.IsDone())
{
var currentSong = iterator.Next() as Song;
Console.WriteLine($"Now Playing:{ currentSong.Name},Singer:{currentSong.Singer}");
}
Console.ReadLine();
}
}
运行结果:
在C#中,我们可以使用foreach语句轻松遍历实现IEnumerable和IEnumerator接口的集合。
List<Song> songs = new List<Song>();
songs.Add(new Song { Name = "Dance To This", Singer = "Troye Sivan" });
songs.Add(new Song { Name = "Ferrari", Singer = "Bebe Rexha" });
songs.Add(new Song { Name = "Ocean", Singer = "Martin Garrix" });
songs.Add(new Song { Name = "Sober", Singer = "Demi Lovato" });
songs.Add(new Song { Name = "Only You", Singer = "Cheat Codes" });
foreach (var song in songs)
{
Console.WriteLine($"Now Playing:{ song.Name},Singer:{song.Singer}");
}
使用场景
-
访问一个聚合对象的内容而无需暴露它的内部表示。
-
支持对聚合对象的多种遍历。
-
为遍历不同的聚合结构提供一个统一的接口。