1. 程式人生 > >簡述-迭代器模式

簡述-迭代器模式

介紹

一個容器,必然會涉及到遍歷演算法,如果將它封裝在容器中,則增加了複雜度,如果客戶實現,則不那麼方便。所以引入第三方類來幫忙處理,這個類就是迭代器。

又稱為遊標模式,行為型設計模式之一。我的理解是目的在遍歷,弱化容器與遍歷演算法的關係。在容器與訪問類中新增一個迭代器,用於承擔遍歷,又不需要訪問類自行實現。提供一種順序訪問一個容器物件中的各個元素,而又不需要暴露改物件的內部表示。

Android中資料庫cusor則使用到了該模式。各個語言一般都有各自的迭代器實現,開發中很少會自己去實現迭代器了。

UML

組成結構

  • Iterator:迭代器介面,負責定義、訪問、遍歷元素的介面
  • ConcreteIterator:具體迭代器類,實現迭代器介面,並記錄當前位置。
  • Aggregate:容器介面,負責提供建立迭代器角色的介面
  • ConcreteAggregate:具體容器類,具體迭代器角色與該容易相關聯

使用場景

  • 遍歷一個容器物件時

事例

比如有兩個北鼻,他們喜歡的水果不同,咱們想遍歷問問她們喜歡的水果,先看看不用迭代器的方式:

  1. 建立一個水果物件,用於承載。簡易北鼻1和北鼻2物件,她們其中都有自己喜歡的水果列表持有,一個是以陣列方式,一個是以列表方式,並提供獲取陣列和列表的方法:
/**
 * 水果
 */
public class ITeFruit {
    /**
     * 水果名稱
     */
    private String name;

    public ITeFruit(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "ITeFruit{" +
                "name='" + name + '\'' +
                '}';
    }
}

/**
 * 北鼻1水果
 */
public class Beibi1Fruit {
    private List<ITeFruit> iTeFruits = new ArrayList<>();

    public Beibi1Fruit() {
        iTeFruits.add(new ITeFruit("菠蘿"));
        iTeFruits.add(new ITeFruit("香蕉"));
        iTeFruits.add(new ITeFruit("哈密瓜"));
        iTeFruits.add(new ITeFruit("榴蓮"));
        iTeFruits.add(new ITeFruit("火龍果"));
    }
    
    public List<ITeFruit> getiTeFruits(){
        return iTeFruits;
    }
}

/**
 * 北鼻二喜歡的水果
 */
public class Beibi2Fruit {
    private ITeFruit[] beibi2 = new ITeFruit[4];

    public Beibi2Fruit() {
        beibi2[0] = new ITeFruit("蘋果");
        beibi2[1] = new ITeFruit("梨子");
        beibi2[2] = new ITeFruit("芒果");
        beibi2[3] = new ITeFruit("哈密瓜");
    }

    /**
     * 獲取北鼻二喜歡的水果陣列
     *
     * @return 水果陣列
     */
    public ITeFruit[] getBeibi2Fruits() {
        return this.beibi2;
    }
}
  1. 進行遍歷查詢(這個沒用增強for迴圈,那樣看就沒區別了):
//北鼻1,遍歷
        Beibi1Fruit beibi1Fruit = new Beibi1Fruit();
        List<ITeFruit> iTeFruits = beibi1Fruit.getiTeFruits();
        System.out.println("北鼻一喜歡水果:");
        for (int i = 0; i < iTeFruits.size(); i++) {
            System.out.println(iTeFruits.get(i));
        }

        //北鼻2,遍歷
        Beibi2Fruit beibi2Fruit = new Beibi2Fruit();
        ITeFruit[] beibi2Fruits = beibi2Fruit.getBeibi2Fruits();
        System.out.println("北鼻二喜歡水果:");
        for (int i = 0; i < beibi2Fruits.length; i++) {
            System.out.println(beibi2Fruits[i]);
        }

輸出就不貼了,從這裡就能看到,遍歷兩個的水果,使用的方式是不一樣的,那麼對外界來說得知道每個的遍歷方式,相對麻煩了,那麼就引入了,提供一個迭代器來實現。

  1. 建立一個迭代器介面,提供獲取下一物件和是否還有下一個的方法:
/**
 * 迭代器介面
 */
public interface IteratorF {
    /**
     * 是否還有下一個
     *
     * @return
     */
    boolean hasNext();

    /**
     * 下一個
     *
     * @return 有則返回下一個物件,否則返回null
     */
    Object next();
}
  1. 建立北鼻1和北鼻2的迭代器,其中各自實現迭代方法:
/**
 * 北鼻1的迭代器
 */
public class Beibi1Iterator implements IteratorF {
    private List<ITeFruit> iTeFruits = new ArrayList<>();

    private int position;

    public Beibi1Iterator(List<ITeFruit> iTeFruits) {
        this.iTeFruits = iTeFruits;
    }


    @Override
    public boolean hasNext() {
        //如果列表中有個數且當前position還沒到末尾
        return (iTeFruits.size() > 0 && position <= iTeFruits.size() - 1);
    }

    @Override
    public Object next() {
        //獲取一個水果,然後將position+1
        return iTeFruits.get(position++);
    }
}

/**
 * 北鼻2的迭代器
 */
public class Beibi2Iterator implements IteratorF {
    private ITeFruit[] beibi2 = new ITeFruit[4];

    private int position;

    public Beibi2Iterator(ITeFruit[] beibi2) {
        this.beibi2 = beibi2;
    }


    @Override
    public boolean hasNext() {
        //如果陣列中有個數且當前position還沒到末尾
        return (beibi2.length > 0 && position <= beibi2.length - 1);
    }

    @Override
    public Object next() {
        //獲取一個水果,然後將position+1
        return beibi2[position++];
    }
}
  1. 建立容器介面,提供一個獲取迭代器的方法(這裡簡單點,就只提供下獲取迭代器):
/**
 * 容器介面,該介面返回一個迭代器,由容器來實現
 */
public interface Aggregate {
    /**
     * 返回一個迭代器
     *
     * @return 迭代器
     */
    IteratorF iterator();
}
  1. 容器實現迭代器方法:
/**
 * 北鼻1水果
 */
public class Beibi1Fruit implements Aggregate {

    private List<ITeFruit> iTeFruits = new ArrayList<>();

    public Beibi1Fruit() {
        iTeFruits.add(new ITeFruit("菠蘿"));
        iTeFruits.add(new ITeFruit("香蕉"));
        iTeFruits.add(new ITeFruit("哈密瓜"));
        iTeFruits.add(new ITeFruit("榴蓮"));
        iTeFruits.add(new ITeFruit("火龍果"));
    }

    /**
     * 建立一個迭代器並返回
     *
     * @return
     */
    @Override
    public IteratorF iterator() {
        return new Beibi1Iterator(this.iTeFruits);
    }
}

/**
 * 北鼻二喜歡的水果
 */
public class Beibi2Fruit implements Aggregate {
    private ITeFruit[] beibi2 = new ITeFruit[4];

    public Beibi2Fruit() {
        beibi2[0] = new ITeFruit("蘋果");
        beibi2[1] = new ITeFruit("梨子");
        beibi2[2] = new ITeFruit("芒果");
        beibi2[3] = new ITeFruit("哈密瓜");
    }
    
    @Override
    public IteratorF iterator() {
        return new Beibi2Iterator(this.beibi2);
    }
}
  1. 遍歷:
//北鼻1,遍歷
        Beibi1Fruit beibi1Fruit = new Beibi1Fruit();
        IteratorF iterator1 = beibi1Fruit.iterator();

        System.out.println("北鼻一喜歡水果:");
        while (iterator1.hasNext()) {
            System.out.println(iterator1.next());
        }

        //北鼻2,遍歷
        Beibi2Fruit beibi2Fruit = new Beibi2Fruit();
        IteratorF iterator2 = beibi2Fruit.iterator();
        System.out.println("北鼻二喜歡水果:");
        while (iterator2.hasNext()) {
            System.out.println(iterator2.next());
        }

輸出和第一步是一樣的,但是看到遍歷的地方了嗎?這樣子遍歷就完全一樣了,對使用方來說就非常方便了。

優缺點

優點

  • 自身優點單一,非常滿足單一職責原則。

缺點

  • 對類檔案的增加

總結:這個設計模式雖然平時我們一般比較少主動去使用,是因為庫中已經有了,但是它遍歷的設計思想,提供給客戶的方便,內部的單一職責原則,是非常值得借鑑的。