chapter09_管理良好的集合——迭代器與組合模式

  • 迭代器可以讓客戶遍歷集合但無法窺視內(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) 為啥要把組合模式和迭代器模式放在一起呢?

    因為組合模式在需要遍歷集合的時候, 也會使用迭代器

?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容