迭代器可以讓客戶遍歷集合但無法窺視內(nèi)部對象存儲的方式
-
(1) 不使用迭代器會讓遍歷操作無法統(tǒng)一接口
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) 使用迭代器便于統(tǒng)一接口
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(); }一種內(nèi)部是數(shù)組的迭代器的實現(xiàn)
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; } } }這樣, 只需在遍歷集合前創(chuàng)建對應的迭代器即可
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的集合類一般都提供了產(chǎn)生迭代器的方法
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()方法中, 產(chǎn)生了一個ArrayList的內(nèi)部類的對象Itr
public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable { ... private class Itr implements Iterator<E> { ... } ... } -
迭代器模式
提供一種方法順序訪問一個聚合對象中的各個元素, 而又不暴露其內(nèi)部的表示
-
設計原則: 單一責任原則
一個類應該只有一個引起變化的原因
例如在沒有使用迭代器以前, 一個集合類既要維持內(nèi)部的數(shù)據(jù)結構, 又要提供遍歷訪問操作, 就有了多重的責任; 現(xiàn)在使用迭代器將遍歷的責任交給其他的類
-
(1) 當集合中的對象內(nèi)部又嵌套了集合對象時, 在不使用任何設計模式的情況下, 會導致在遍歷集合的時候, 需要單獨判斷當前對象的類型再做操作
(2) 為了解決(1)中的問題, 可以定義一個超類, 讓單個對象和對象集合都繼承這個超類;
在超類中定義抽象方法, 然后由單個對象的類和對象集合類實現(xiàn);
這樣, 整個就會組織成一個樹形結構, 可以容納菜單(超類)、子菜單(對象集合)、菜單項(單個對象)
(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是一個超類, 定義了一系列默認方法的實現(xiàn), 這些實現(xiàn)中都會拋出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是具體的組件類, 它實現(xiàn)了它可以實現(xiàn)的方法, 調(diào)用它未實現(xiàn)的方法就會調(diào)用它的基類的方法從而拋出異常
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是菜單類, 它內(nèi)部包含一個存儲MenuComponent的集合, 在print()方法中, 會調(diào)用內(nèi)部MenuComponent的print()方法
-
組合模式
將對象組合成樹形結構來表現(xiàn)整體/部分層次結構。 組合可以讓客戶以一致的方式處理個別對象以及對象組合
-
(1) 組合模式保證了透明性: 客戶在操作是將組合對象和葉結點一視同仁, 一個元素是組合對象還是葉結點對客戶是透明的
(2) 保證透明性的同時產(chǎn)生了安全性問題, 因為調(diào)用時很可能會調(diào)用拋出UnsupportedException的方法。 解決方法是將組合對象和葉結點定義成不同接口, 用instanceof判斷;
但是, 這樣又喪失了透明性.
所以, 組合模式就是透明性和安全性的折中
(3) 為啥要把組合模式和迭代器模式放在一起呢?
因為組合模式在需要遍歷集合的時候, 也會使用迭代器