迭代器模式(Iterator Pattern)
概念:
定义:迭代器模式 提供一种方法顺序访问一个聚合对象中的各个元素,而又不暴露其内部的表示。
迭代器模式是一种简单常见的设计模式,在我们使用编程语言中的集合容器都会有迭代器。
组成:
Aggregate(抽象聚合):共同的接口供所有的聚合使用。
ConcreteAggregate(聚合):持有一个对象的集合,并实现 createIterator 方法,返回集合的迭代器。
Iterator(抽象迭代器接口):包含所有迭代器都必须实现的方法。
ConcreteIterator(具体迭代器):实现了迭代器接口的具体迭代器。
例子:
现有两家超市,一家为水果超市,一家为零食超市。他们分别请了两位编程人员帮他们实现打印品种清单程序。结果第一家编程人员用数组集合来存储不同的水果,第二家编程人员用 ArrayList 集合来存储零食种类。如下:
物品类:
public class Item {
private String name;
private String description;
double price;
public Item(String name, String description, double price) {
this.name = name;
this.description = description;
this.price = price;
}
public String getName() {
return name;
}
public String getDescription() {
return description;
}
public double getPrice() {
return price;
}
}
水果超市类:
public class FruitSupermarket {
ArrayList menuItems;
public FruitSupermarket() {
menuItems = new ArrayList();
//添加水果
addItem("苹果", "红色的", 1.99);
addItem("香蕉", "黄色的", 2.00);
addItem("橙子", "横色的", 3.12);
}
public void addItem(String name, String description, double price) {
Item item = new Item(name, description, price);
menuItems.add(item);
}
public ArrayList getMenuItems() {
return menuItems;
}
}
零食超市类:
public class SnacksSupermarket {
static final int MAX_ITEMS = 6;
int numberOfItems = 0;
Item[] menuItems;
public SnacksSupermarket() {
menuItems = new Item[MAX_ITEMS];
//添加零食
addItem("牛奶", "伊利", 2.24);
addItem("糖", "阿尔卑斯", 5.12);
addItem("巧克力", "金蒂", 1.23);
}
public void addItem(String name, String description, double price) {
Item item = new Item(name, description, price);
if (numberOfItems >= MAX_ITEMS) {
System.out.println("抱歉,清单已满...");
} else {
menuItems[numberOfItems] = item;
numberOfItems += 1;
}
}
public Item[] getMenuItems() {
return menuItems;
}
}
现在假设两家超市合并了,我们要遍历两家超市的清单。
public class Supermarket {
FruitSupermarket fruitSupermarket;
SnacksSupermarket snacksSupermarket;
public Supermarket() {
fruitSupermarket = new FruitSupermarket();
snacksSupermarket = new SnacksSupermarket();
}
//遍历菜单方法,内部分别包含两家超市的遍历。
public void printMenu() {
ArrayList fruitItems = fruitSupermarket.getMenuItems();
Item[] snacksItems = snacksSupermarket.getMenuItems();
//遍历水果超市
System.out.println("fruitItem:");
for (int i = 0; i < fruitItems.size(); ++i) {
Item item = (Item) fruitItems.get(i);
System.out.print("name:" + item.getName());
System.out.print(" description:" + item.getDescription());
System.out.println(" price:" + item.getPrice());
}
//遍历零食超市
System.out.println("\nsnacksItem:");
for (int i = 0; i < snacksSupermarket.getNumberOfItems(); ++i) {
Item item = snacksItems[i];
System.out.print("name:" + item.getName());
System.out.print(" description:" + item.getDescription());
System.out.println(" price:" + item.getPrice());
}
}
public static void main(String[] args) {
Supermarket supermarket = new Supermarket();
supermarket.printMenu();
}
}
如果将来再次合并超市又必须修改代码,增加一个循环,我们可以发现上述代码成了 面向实现 编程而不是 面向接口。
我们来通过 迭代器模式 改进这个类。
先将两个超市类增加 createIterator() 方法,返回内部集合的迭代器。
由于 ArrayList 本身有迭代器,我们直接返回即可。
//省略原本类的方法
public Iterator createIterator() {
return menuItems.iterator();
}
但零食超市中的数组集合没有迭代器,我们自己实现一个。
//Java 提供了迭代器接口,我们直接实现即可
public class SnacksIterator implements Iterator {
Item[] menuItems;
int position = 0;
public SnacksIterator(Item[] menuItems) {
this.menuItems = menuItems;
}
//判断是否有下一个元素方法
public boolean hasNext() {
if (position >= menuItems.length || menuItems[position] == null) {
return false;
}else {
return true;
}
}
//返回下一个对象方法
public Object next() {
Item menuitem = menuItems[position];
position += 1;
return menuitem;
}
}
//...省略原本类和方法
public Iterator createIterator() {
return new SnacksIterator(menuItems);
}
超市类:
public class Supermarket {
FruitSupermarket fruitSupermarket;
SnacksSupermarket snacksSupermarket;
public Supermarket() {
fruitSupermarket = new FruitSupermarket();
snacksSupermarket = new SnacksSupermarket();
}
//由于实现了统一的接口,再次添加我们继续创建对应的迭代器类,加入 printMenu 即可。
public void printMenu() {
//我们也可以将所有的清单加入集合中,这样下面的代码也不用改变了。
Iterator fruitIterator = fruitSupermarket.createIterator();
Iterator snacksIterator = snacksSupermarket.createIterator();
System.out.println("FruitSupermarket");
printMenu(fruitIterator);
System.out.println("\nSnacksSupermarket");
printMenu(snacksIterator);
}
private void printMenu(Iterator iterator) {
while (iterator.hasNext()) {
Item item = (Item) iterator.next();
System.out.print("name:" + item.getName());
System.out.print(" description:" + item.getDescription());
System.out.println(" price:" + item.getPrice());
}
}
public static void main(String[] args) {
Supermarket supermarket = new Supermarket();
supermarket.printMenu();
}
}
适用场景:
- 访问一个聚合对象的内容而无需暴露它的内部表示。
- 需要为聚合对象提供多种遍历方式。
- 为遍历不同的聚合结构提供一个统一的接口 (即, 支持多态迭代)
补充:设计原则
单一设计原则:一个类应该只有一个引起变化的原因
我们应该避免类内的改变,因为修改代码很容易造成许多潜在的错误。如果一个类具有两个改变的原因,那么类变化的几率就很大了,并且当它真的改变时,我们的设计中会有两方面受到影响。当一个模块或一个类被设计成只支持一组相关的功能时,我们说它具有高内聚。反之,当被设计成支持一组不相关的功能时,我们说它具有低内聚。