-
迭代器可以让客户遍历集合但无法窥视内部对象存储的方式
-
(1) 不使用迭代器会让遍历操作无法统一接口
public static void printMenu() { PancakeHouseMenu pancakeHouseMenu = new PancakeHouseMenu(); DinerMenu dinerMenu = new DinerMenu(); ArrayList<MenuItem> breakfastItems = pancakeHouseMenu.getMenuItems(); MenuItem[] lunchItems = dinerMenu.getMenuItems(); // Exposing implementation System.out.println("USING FOR LOOPS"); for (int i = 0; i < breakfastItems.size(); i++) { MenuItem menuItem = (MenuItem) breakfastItems.get(i); System.out.print(menuItem.getName()); System.out.println("\t\t" + menuItem.getPrice()); System.out.println("\t" + menuItem.getDescription()); } for (int i = 0; i < lunchItems.length; i++) { MenuItem menuItem = lunchItems[i]; System.out.print(menuItem.getName()); System.out.println("\t\t" + menuItem.getPrice()); System.out.println("\t" + menuItem.getDescription()); } }
(2) 使用迭代器便于统一接口
public void printMenu() { Iterator pancakeIterator = pancakeHouseMenu.createIterator(); Iterator dinerIterator = dinerMenu.createIterator(); System.out.println("MENU\n----\nBREAKFAST"); printMenuHelper(pancakeIterator); System.out.println("\nLUNCH"); printMenuHelper(dinerIterator); } private void printMenuHelper(Iterator iterator) { while (iterator.hasNext()) { MenuItem menuItem = iterator.next(); System.out.print(menuItem.getName() + ", "); System.out.print(menuItem.getPrice() + " -- "); System.out.println(menuItem.getDescription()); } }
-
迭代器中一般包含两个方法
public interface Iterator { boolean hasNext(); MenuItem next(); }
一种内部是数组的迭代器的实现
public class DinerMenuIterator implements Iterator { private MenuItem[] items; private int position = 0; public DinerMenuIterator(MenuItem[] items) { this.items = items; } public MenuItem next() { MenuItem menuItem = items[position]; position = position + 1; return menuItem; } public boolean hasNext() { if (position >= items.length || items[position] == null) { return false; } else { return true; } } }
这样, 只需在遍历集合前创建对应的迭代器即可
public Iterator createIterator() { return new DinerMenuIterator(menuItems); }
-
Java本身提供了Iterator接口
public interface Iterator<E> { boolean hasNext(); E next(); default void remove() { throw new UnsupportedOperationException("remove"); } default void forEachRemaining(Consumer<? super E> action) { Objects.requireNonNull(action); while (hasNext()) action.accept(next()); } }
因此, Java的集合类一般都提供了产生迭代器的方法
ArrayList.java
public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable { ... public Iterator<E> iterator() { return new Itr(); } ... }
Iterator()方法中, 产生了一个ArrayList的内部类的对象Itr
public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable { ... private class Itr implements Iterator<E> { ... } ... }
-
迭代器模式
提供一种方法顺序访问一个聚合对象中的各个元素, 而又不暴露其内部的表示
-
设计原则: 单一责任原则
一个类应该只有一个引起变化的原因
例如在没有使用迭代器以前, 一个集合类既要维持内部的数据结构, 又要提供遍历访问操作, 就有了多重的责任; 现在使用迭代器将遍历的责任交给其他的类
-
(1) 当集合中的对象内部又嵌套了集合对象时, 在不使用任何设计模式的情况下, 会导致在遍历集合的时候, 需要单独判断当前对象的类型再做操作
(2) 为了解决(1)中的问题, 可以定义一个超类, 让单个对象和对象集合都继承这个超类;
在超类中定义抽象方法, 然后由单个对象的类和对象集合类实现;
这样, 整个就会组织成一个树形结构, 可以容纳菜单(超类)、子菜单(对象集合)、菜单项(单个对象)
(3) 示例
MenuComponent.java
public abstract class MenuComponent { public void add(MenuComponent menuComponent) { throw new UnsupportedOperationException(); } public void remove(MenuComponent menuComponent) { throw new UnsupportedOperationException(); } public MenuComponent getChild(int i) { throw new UnsupportedOperationException(); } public String getName() { throw new UnsupportedOperationException(); } public String getDescription() { throw new UnsupportedOperationException(); } public double getPrice() { throw new UnsupportedOperationException(); } public boolean isVegetarian() { throw new UnsupportedOperationException(); } public void print() { throw new UnsupportedOperationException(); } }
MenuComponent是一个超类, 定义了一系列默认方法的实现, 这些实现中都会抛出UnsupportedOperationException异常;
MenuItem.java
public class MenuItem extends MenuComponent { private String name; private String description; private boolean vegetarian; private double price; public MenuItem(String name, String description, boolean vegetarian, double price) { this.name = name; this.description = description; this.vegetarian = vegetarian; this.price = price; } public String getName() { return name; } public String getDescription() { return description; } public double getPrice() { return price; } public boolean isVegetarian() { return vegetarian; } public void print() { System.out.print(" " + getName()); if (isVegetarian()) { System.out.print("(v)"); } System.out.println(", " + getPrice()); System.out.println(" -- " + getDescription()); } }
MenuItem是具体的组件类, 它实现了它可以实现的方法, 调用它未实现的方法就会调用它的基类的方法从而抛出异常
Menu.java
public class Menu extends MenuComponent { private ArrayList<MenuComponent> menuComponents = new ArrayList<MenuComponent>(); private String name; private String description; public Menu(String name, String description) { this.name = name; this.description = description; } public void add(MenuComponent menuComponent) { menuComponents.add(menuComponent); } public void remove(MenuComponent menuComponent) { menuComponents.remove(menuComponent); } public MenuComponent getChild(int i) { return menuComponents.get(i); } public String getName() { return name; } public String getDescription() { return description; } public void print() { System.out.print("\n" + getName()); System.out.println(", " + getDescription()); System.out.println("---------------------"); Iterator<MenuComponent> iterator = menuComponents.iterator(); while (iterator.hasNext()) { MenuComponent menuComponent = iterator.next(); menuComponent.print(); } } }
Menu是菜单类, 它内部包含一个存储MenuComponent的集合, 在print()方法中, 会调用内部MenuComponent的print()方法
-
组合模式
将对象组合成树形结构来表现整体/部分层次结构。 组合可以让客户以一致的方式处理个别对象以及对象组合
-
(1) 组合模式保证了透明性: 客户在操作是将组合对象和叶结点一视同仁, 一个元素是组合对象还是叶结点对客户是透明的
(2) 保证透明性的同时产生了安全性问题, 因为调用时很可能会调用抛出UnsupportedException的方法。 解决方法是将组合对象和叶结点定义成不同接口, 用instanceof判断;
但是, 这样又丧失了透明性.
所以, 组合模式就是透明性和安全性的折中
(3) 为啥要把组合模式和迭代器模式放在一起呢?
因为组合模式在需要遍历集合的时候, 也会使用迭代器
网友评论