写在前面的话
这章为什么叫不只是迭代器模式呢?首先声明我不是标题党,不是为了引起注意。古人说认知东西的三种境界,看山是山,看山不是山,看山还是山,认知总是要有一个过程才行。而最近在我研究设计模式的过程中就有一种学进去没跳出来的感觉,用和尚的话来说就是着相了。
学进去和跳出来
最近写了几篇有关Android源码和设计模式的博客,然后发现最近的思考方式有点过于注意细枝末节了。而我认识一个东西的习惯是在关注细节的同时也必须要对这个东西在宏观上有一个认知。既要能学进去,又要能跳出来。所以我们不妨先跳出来,复习一下设计模式的概况。
什么是设计模式
在软件工程中,设计模式(design pattern)是对软件设计中普遍存在的各种问题,所提出的解决方案。设计模式并不是固定的一套代码,而是针对某一特定问题的具体解决思路与方案。可以认为是一种最佳实践,因为他是无数软件开发人员经过长时间的实践总结出来的。
为什么要学习设计模式
复用解决方案:模式是对一个通用设计问题可以复用的解决方式。
确立通用术语:模式在开发者沟通之间提供了一机制,允许开发者在更高的层次沟通而不是代码层面,并且目标是理解被开发的代码。
设计模式的具体作用:减少各个分析类之间的耦合和依赖。使软件更容易修改和维护,更善于应对变化。
其实在我们每看一个设计模式的时候要记得无论它是哪种模式,它的初衷都是一样的。我们不一定要用这些设计模式,也不一定要生搬硬套这些设计,我们要做的是解决问题,只不过这些模式在解决这些问题上更成熟,所以才被大家广泛使用。
如果我们没有遇到设计模式的使用场景,或者之后的变化也不会用到,就不要去思考如何用设计模式优化。
接下来开始今天的正题,看下我们的迭代器模式。
定义
提供一种方法访问一个容器对象中各个元素,而又不需要暴露对象的内部细节。是一种行为型设计模式。
使用场景
迭代器模式是与集合共生共死的,一般来说,我们只要实现一个集合,就需要同时提供这个集合的迭代器,就像java中的Collection,List、Set、Map等,这些集合都有自己的迭代器。假如我们要实现一个这样的新的容器,当然也需要引入迭代器模式,给我们的容器实现一个迭代器。
但是,由于容器与迭代器的关系太密切了,所以大多数语言在实现容器的时候都给提供了迭代器,并且这些语言提供的容器和迭代器在绝大多数情况下就可以满足我们的需要,所以现在需要我们自己去实践迭代器模式的场景还是比较少见的,我们只需要使用语言中已有的容器和迭代器就可以了。
结构
模式所涉及的角色有:
迭代器角色(Iterator):迭代器角色负责定义访问和遍历元素的接口。
具体迭代器角色(ConcreteIterator):具体迭代器角色要实现迭代器接口,并要记录遍历中的当前位置。
容器角色(Aggregate):容器角色负责提供创建具体迭代器角色的接口。
具体容器角色(ConcreteAggregate):具体容器角色实现创建具体迭代器角色的接口。这个具体迭代器角色与该容器的结构相关。
实现
这里我们用一个现实中的例子来演示一下迭代器模式。场景是这样的:我们要创办一个提供家具一站式服务的公司,但是我们并不自己生产家具,我们有自己获取家具的渠道。可是每个渠道存储他们产品的数据结构可能是不一样的,我们怎样才能方便的访问他们的产品呢?
不知道大家发现没,大部分设计模式解耦或者封装变化的方式都是加了一个中间层。迭代器模式也是这样的,Iterator就是一个中间层,封装了各个数据结构遍历的变化。
迭代器角色
public interface Iterator
{ T next(); boolean hasNext(); }
具体电视厂商的迭代器角色
public class TVIterator implements Iterator
{ private List
list = new ArrayList
(); private int cursor = 0; public TVIterator(TVCompany tvCompany) { list = tvCompany.getTvList(); } @Override public TVBean next() { TVBean tv = null; if (this.hasNext()) { tv = this.list.get(cursor++); } return tv; } @Override public boolean hasNext() { if (cursor == list.size()) { return false; } return true; } }
容器角色
public interface Aggregate
{ void add(T t); void remove(T t); }
具体电视容器类角色
public class TVCompany implements Aggregate
{ private List
tvList; public TVCompany() { tvList = new ArrayList<>(); } public List
getTvList() { return tvList; } @Override public void add(TVBean tvBean) { tvList.add(tvBean); } @Override public void remove(TVBean tvBean) { tvList.remove(tvBean); } }
测试方法
private void testIterator() {
TVCompany tvCompany = new TVCompany();
tvCompany.add(new TVBean(0, "15寸"));
tvCompany.add(new TVBean(1, "16寸"));
tvCompany.add(new TVBean(2, "21寸"));
tvCompany.add(new TVBean(3, "25寸"));
tvCompany.add(new TVBean(4, "30寸"));
TVIterator tvIterator = new TVIterator(tvCompany);
while (tvIterator.hasNext()) {
Log.e("testIterator", tvIterator.next().getName());
}
}
依次类推,我们的空调厂商提供空调、冰箱厂商提供冰箱都是一样的道理。但是对于我们这个公司来说,我们需要关心的只有是否有下一个hasNext()和取出下一个next()。
Android源码中的迭代器模式
就像在使用场景中说的那样,一般我们不会去自己实现一个迭代器,Android中用到迭代器的场景比较典型的场景是类集框架的遍历和数据库的Cursor。
在前面的Android中的工厂方法模式中,我们说Java的类集框架用到了工厂模式,现在我们再来看一下它的设计结构:
我们知道Iterator是迭代器,用来遍历一个集合中的元素。而不同的数据结构遍历的方式是不一样的,所以迭代器的实现也是不同的。每一个集合内部都会有一个Iterator的实现。使用工厂方法模式将迭代器的具体类型延迟到具体容器类中,比较灵活,容易扩展。
这个模式比较简单,我们就不做过多说明了。搞定,收工。
测试代码已上传到github。