一、迭代器模式介绍
迭代器模式(Iterator Pattern)又称为(Cursor)模式,是行为设计模式之一。迭代器模式算是一个比较古老的设计模式,其源于对容器的访问,比如Java 中的 List、Map、数组等,我们知道对容器对象的访问必然会涉及遍历算法,我们可以将遍历的方法封装在容器中,或者不提供遍历方法。如果我们将遍历的方法封装到容器中,那么对于容器来说就承担了过多的功能,容器类不仅要维护自身内部的数据元素而且还要对外提供遍历的接口方法,因为遍历状态的存储问题还不能对同一个容器同时进行多个遍历操作,如果我们不提供遍历方法而让使用者自己去实现,又会让容器的内部细节暴露无遗,正因为于此,迭代模式应用而生,在客户访问类与容器体之间插入了一个第三者–迭代器,很好的解决了上面所述的弊端。
二、迭代器模式的定义
提供一种方法顺序访问一个容器对象中的各个元素,而又不需要暴露该对象的内部表示。
三、迭代器模式的使用场景
遍历一个容器对象时。
四、迭代器模式的UML类图
根据类图可以得出如下一个迭代器模式的通用模式代码。
//1、迭代器接口
public interface Iterator<T> {
/**
* 是否还有下一个元素
* @return true 表示有,false 表示没有
*/
boolean hasNext();
/**
* 返回当前位置的元素并将位置移至下一位
* @return
*/
T next();
}
//2、具体迭代器类
public class ConcreteIterator<T> implements Iterator<T> {
private List<T> list = new ArrayList<T>();
private int cursor = 0;
public ConcreteIterator(List<T> list) {
this.list = list;
}
@Override
public boolean hasNext() {
return cursor != list.size();
}
@Override
public T next() {
T obj = null;
if (this.hasNext()) {
obj = this.list.get(cursor++);
}
return obj;
}
}
//3、容器接口
public interface Aggregate<T> {
/**
* 添加一个元素
* @param obj 元素对象
*/
void add(T obj);
/**
* 移除一个元素
* @param obj
*/
void remove(T obj);
/**
* 获取容器的迭代器
* @return
*/
Iterator<T> iterator();
}
//4、具体容器类
public class ConcreteAggregate<T> implements Aggregate<T> {
private List<T> list = new ArrayList<T>();
@Override
public void add(T obj) {
list.add(obj);
}
@Override
public void remove(T obj) {
list.remove(obj);
}
@Override
public Iterator<T> iterator() {
return new ConcreteIterator<T>(list);
}
}
//5、客户类
public class Client {
public static void main(String[] args) {
Aggregate<String> a = new ConcreteAggregate<>();
a.add("Aige");
a.add("Studio");
a.add("SM");
a.add("Brother");
Iterator<String> i = a.iterator();
while (i.hasNext()) {
System.out.println(i.next());
}
}
}
运行结果:
角色介绍:
1、Iterator: 迭代器接口。
迭代器接库负责定义、访问和遍历元素的接口。
2、ConcreteIterator:具体迭代器类。
具体迭代器类的目的主要是实现迭代器接口,并记录遍历的当前位置。
3、Aggregate: 容器接口。
容器接口负责提供创建具体迭代器角色的接口。
4、ConcreteAggregate:具体容器类。
具体迭代器角色与该容器相关联。
5、Client: 客户类。
五、迭代器模式的简单实现
小明和小辉分别在公司的两个事业部,老板安排任务,让他们俩统计一下各自部门的员工数据。
先为员工创建一个实体类:
public class Employee {
private String name;
private int age;
private String sex;
private String position;
public Employee(String name, int age, String sex, String position) {
super();
this.name = name;
this.age = age;
this.sex = sex;
this.position = position;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public String getPosition() {
return position;
}
public void setPosition(String position) {
this.position = position;
}
@Override
public String toString() {
return "Employee [name=" + name + ", age=" + age + ", sex=" + sex + ", position=" + position + "]";
}
}
定义一个迭代器接口:
public interface Iterator {
/**
* 是否还有下一个元素
* @return true 表示有,false 表示没有
*/
boolean hasNext();
/**
* 返回当前位置的元素,并将位置移至下一位
* @return
*/
Object next();
}
对于小明和小辉部门的人员信息容器,我们分别创建一个对应的迭代器:
public class MinIterator implements Iterator {
private List<Employee> list;
private int position;
public MinIterator(List<Employee> list) {
this.list = list;
}
@Override
public boolean hasNext() {
return !(position > list.size() - 1 || list.get(position) == null);
}
@Override
public Object next() {
Employee employee = list.get(position);
position++;
return employee;
}
}
同时,我们定义一个容器类的接口,在该接口中定义一个能够返回容器迭代器的方法:
public interface Company {
/**
* 返回一个迭代器对象
* @return 迭代器对象
*/
Iterator iterator();
}
这时给出两个容器类使之实现容器接口,并返回对应的迭代器对象:
小明和小辉两个部门间人员数据的构建,对于小明的部门,小明创建一个 CompanyMin 类来存储人员信息,这里为了简化代码,我们不提供对外添加人员信息的add方法,只在构造方法中固定数据:
public class CompanyMin implements Company {
private List<Employee> list = new ArrayList<>();
public CompanyMin() {
list.add(new Employee("小明", 26, "男", "程序员"));
list.add(new Employee("小云", 26, "女", "测试"));
list.add(new Employee("小方", 26, "女", "测试"));
list.add(new Employee("可儿", 26, "女", "运营"));
list.add(new Employee("安琪拉", 26, "女", "设计"));
}
public List<Employee> getEmployee(){
return list;
}
@Override
public Iterator iterator() {
return new MinIterator(list);
}
}
而对于小辉,同样也创建了一个CompanyHui类作为容器:
public class CompanyHui implements Company {
private Employee[] array = new Employee[3];
public CompanyHui() {
array[0] = new Employee("辉哥", 108, "男","程序员");
array[1] = new Employee("小红", 98, "男","程序员");
array[2] = new Employee("小辉", 88, "男","程序员");
}
public Employee[] getEmployees(){
return array;
}
@Override
public Iterator iterator() {
return new HuiIterator(array);
}
}
最后在Boss 类中查阅人员的信息将变得更简单:
public class Boss {
public static void main(String[] args) {
CompanyMin min = new CompanyMin();
check(min.iterator());
System.out.println("====================");
CompanyHui hui = new CompanyHui();
check(hui.iterator());
}
private static void check(Iterator iterator) {
while (iterator.hasNext()) {
System.out.println(iterator.next().toString());
}
}
}
运行结果:
其实,在上述例子中只是做了一个假设,在所谓的自定义“容器”类中又使用了 Java 本身所提供的数据结构来存储数据,这也算是一种不恰当的实现方式,因为迭代器模式的规定不像其他模式那么严格,实现也因人而异,好在大部分高级语言的容器类都为我们提供了相应的迭代器而不需要开发者去实现。
六、Android源码中的设计模式
迭代器这个模式对开发者来说几乎不会自己去实现一个迭代器,就拿Android来说吧,其除了各种数据结构体,如List 、Map 等所包含的迭代器外,Android自身源码中也为我们提供了迭代器遍历数据,最为典型的例子就是数据库查询使用Cursor,当我们使用SQLiteDatabase 的 query 方法查询数据库时,会返回一个Cursor 游标对象,该游标对象实质就是一个具体的迭代器,我们可以使用它来遍历数据库查询所得的结果集,这里给出一个简单的查询SQLite数据库的例子。首先定义一个SQLiteOpenHelper:
public class DBHelper extends SQLiteOpenHelper {
public DBHelper(Context context) {
super(context,"DB_AIGE",null,1);
}
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL("CREATE TABLE table_aige(_id INTEGER PRIMARY KEY AUTOINCREMENT,name TEXT,sex TEXT)");
db.execSQL("INSERT INTO table_aige(name,sex) values ('Aige','man')");
db.execSQL("INSERT INTO table_aige(name,sex) values ('SMBrother','man')");
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
}
为了方便起见,直接在 onCreate 方法中创建表并直接插入了两条数据,同时,也是为了简便起见,就不再创建数据库操作相关的业务逻辑方法了,这里直接用一个 ContentProvider 来对数据库进行操作:
public class DataProvider extends ContentProvider {
private DBHelper mDBHelper;
@Override
public boolean onCreate() {
mDBHelper = new DBHelper(getContext());
return true;
}
@Nullable
@Override
public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection,
@Nullable String[] selectionArgs, @Nullable String sortOrder) {
SQLiteDatabase db = mDBHelper.getReadableDatabase();
return db.query("table_aige",projection,null,null,null,null,null);
}
@Nullable
@Override
public String getType(@NonNull Uri uri) {
return null;
}
@Nullable
@Override
public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {
return null;
}
@Override
public int delete(@NonNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs) {
return 0;
}
@Override
public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String selection, @Nullable String[] selectionArgs) {
return 0;
}
}
这里我们只实现了查询方法,其余均保持默认,当然,作为四大组件之一,我们还需要在AndroidManifest.xml文件中对该ContentProvider 进行声明:
<provider
android:name=".DataProvider"
android:authorities="com.aigestudio.test.DataProvider" />
最后我们在Activity 中通过这个ContentProvider 查询数据库:
public class IteratorActivity extends ListActivity {
private static final String[] PROJECTION = new String[]{"name","sex"};
private static final Uri URI = Uri.parse("content://com.aigestudio.test.DataProvider/table_aige");
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Cursor cursor = getContentResolver().query(URI,PROJECTION,null,null,null);
List<Map<String,String>> list = new ArrayList<>();
cursor.moveToFirst();
do {
Map<String,String> item = new HashMap<>();
item.put("name",cursor.getString(0));
item.put("sex",cursor.getString(1));
list.add(item);
}while (cursor.moveToNext());
cursor.close();
setListAdapter(new SimpleAdapter(
this,
list,
android.R.layout.simple_list_item_2,
new String[]{"name","sex"},
new int[]{android.R.id.text1,android.R.id.text2}
));
}
}
具体的显示结果如下图:
七、总结
对于迭代模式来说,其自身优点明显也很单一,支持以不同的方式去遍历一个容器对象,也可以有多个遍历,弱化了容器类与遍历算法之间的关系,而其缺点就是对类文件的增加。
大家也可能会想到其他的语言,如 C++、Python、PHP等,他们内部也有众多容器体的定义,当然,也有相应的迭代器。迭代器模式发展至今,几乎每一种高级语言都有相应的内置实现,对于开发者而言,已经极少会去由自己来实现迭代器了,因此,对于本章的内容更多地是在于了解而非应用。