概述
文章目录
- 迭代器模式
- 结构
- 实现
- 应用场景
- 优缺点
- 与其他模式的关系
迭代器模式
迭代器模式是对象行为型模式,它提供一个对象来顺序访问聚合对象中的元素(遍历元素的算法),且不暴露底层实现。
集合是编程中非常常见的数据结构之一。对于不同数据类型所需要的不同的遍历方式。但大多数客户端并不关心数据存储的方式。所以由于集合的遍历方式的不同,我们需要将集合遍历的算法,从集合中抽取出来,而抽取的部分,就是迭代器。
迭代器通常会提供获取集合元素的基本方法。客户端通过不断调用此方法,直至遍历结束。在 C# 中 foreach
就是对于迭代器遍历元素的具体实现。foreach 需要集合类继承 IEnumerable
接口,IEnumerable
只有一个方法就是返回 集合枚举器(即是迭代器)IEnumerator
结构
说明
- 抽象迭代器(Iterator)- 接口需要声明遍历聚合操作:一个获得元素的方法,一个获得当前位置的方法。
- 具体迭代器(Concrete Iterator)- 实现具体遍历的算法
- 抽象聚合(Aggregate)- 接口需声明一个迭代器接口(返回值为迭代器),其余集合的常见操作可以有选择的声明
- 具体聚合(Concrete Aggregate)- 实现抽象方法,也可以自行实现一些具体的集合操作。
说明
在 C# 中集合类几乎均是采用迭代器模式,如 List,Queue,Stack,Dictionary等。
迭代器接口为IEnumerator
和 泛型接口IEnumerator<T>
聚合接口为IEnumerable
和 泛型接口IEnumerable<T>
我建议自定义实现的集合,若无特殊情况,都去实现 IEnumerable<T>
和 IEnumerator<T>
这两个泛型接口。因为 C# 中的许多原生的特性和函数都是围绕着两个接口实现的,当我们去实现这两个接口时,有许多的 C# 特性可以直接套用即可。
如 foreach
遍历 和 Linq 语句等
实现
这里我们将直接套用泛型接口 IEnumerable
和 IEnumerator
这里来我们封装 GameObject 的集合
游戏对象枚举器(具体迭代器)
public class GameObjectEnum : IEnumerator<GameObject>
{
private IList<GameObject> _list;
private int _index = -1;
public GameObjectEnum(IList<GameObject> list)
{
_list = list;
}
public bool MoveNext()
{
_index++;
return _index < _list.Count;
}
public void Reset() => _index = -1;
public GameObject Current
{
get
{
try
{
return _list[_index];
}
catch (IndexOutOfRangeException)
{
throw new IndexOutOfRangeException();
}
}
}
object IEnumerator.Current => Current;
public void Dispose() { }
}
游戏对象集合(具体聚合类)
public class GameObjectList : IEnumerable<GameObject>
{
private IList<GameObject> _list;
public int Count => _list.Count;
public GameObject this[int index]
{
get => _list[index];
set => _list[index] = value;
}
public GameObjectList()
{
_list = new List<GameObject>();
}
public void Add(GameObject item) => _list.Add(item);
public void Remove(GameObject item) => _list.Remove(item);
public void RemoveAt(int index) => _list.RemoveAt(index);
public GameObject GetRandom() => _list[Random.Range(0, _list.Count)];
public IEnumerator<GameObject> GetEnumerator()
{
return new GameObjectEnum(_list);
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
调用端
public class IteratorExample : MonoBehaviour
{
[SerializeField] private GameObject[] _gameObjects;
private GameObjectList _list;
private void Start()
{
_list = new GameObjectList();
foreach (var go in _gameObjects)
{
_list.Add(go);
}
}
private void OnGUI()
{
GUILayout.BeginArea(new Rect(10, 10, 300, 1000));
//随机获得一个对象
if (GUILayout.Button("Random", GUILayout.Height(120)))
_list.GetRandom().GetComponent<MeshRenderer>().material.color = Color.red;
//遍历对象,使用协程来达到可视化效果
if (GUILayout.Button("Foreach", GUILayout.Height(120)))
{
if (_coroutine != null)
{
foreach (var go in _list)
{
go.GetComponent<MeshRenderer>().material.color = Color.white;
}
StopCoroutine(_coroutine);
}
_coroutine = StartCoroutine(nameof(ForeachList));
}
//还原为白色
if (GUILayout.Button("Reduction", GUILayout.Height(120)))
{
foreach (var go in _list)
{
go.GetComponent<MeshRenderer>().material.color = Color.white;
}
}
GUILayout.EndArea();
}
private Coroutine _coroutine;
private IEnumerator ForeachList()
{
foreach (var go in _list)
{
yield return new WaitForSeconds(0.4f);
go.GetComponent<MeshRenderer>().material.color = Color.green;
}
}
}
这里可以自行去测试一下,并且由于实现了
IEnumerable<T>
同时也是可以使用 Linq 语句查询的
应用场景
- 当集合为复杂的数据类型时,需要隐藏其实现
- 希望动态的切换遍历算法时,如 DFS遍历树,BFS遍历树等。
优缺点
优点
- 复杂的遍历过程抽取成单独的类,满足单一职责原则
- 当使用新的迭代算法时,不会影响原来的类
- 支持动态切换迭代算法
缺点
- 迭代器的遍历不如直接遍历高效
- 会提升系统复杂度
与其他模式的关系
- 组合模式可以使用迭代器模式进行遍历
- 使用工厂模式可以创建不同迭代器
最后
以上就是瘦瘦水杯为你收集整理的迭代器模式 - Unity迭代器模式的全部内容,希望文章能够帮你解决迭代器模式 - Unity迭代器模式所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复