設計模式讀書筆記-----迭代器模式

      你項目組接到一個項目:對電視機的電視頻道、電影和收音機菜單進行統一管理,創建一個統一的菜單管理界面,可以看到全部的電視界面、電影界面和收音機頻道。你有三個手下:小李子、小杏子、小安子,他們分別就每一個模塊作開發工做,看他們都作了哪些工做。java

      這是主菜單JavaBean,用於顯示每一個模塊的菜單。數組

public class MenuItem {
    private String name;
    private String description;
    private int channe;
    
    public MenuItem(int channe,String name,String description){
        this.name = name;
        this.description = description;
        this.channe = channe;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    public int getChanne() {
        return channe;
    }

    public void setChanne(int channe) {
        this.channe = channe;
    }
    
}

      小李子負責電視頻道菜單的實現。他是使用List實現的,他認爲這樣就能夠很是方便的擴展菜單。函數

public class TVChanneMenu implements TelevisionMenu{
    List<MenuItem> menuItems;
    
    /**
     * 構造函數完成初始化
     */
    public TVChanneMenu(){
        menuItems = new ArrayList<MenuItem>();
        addItem(1, "CCTV-1", "This is CCTV-1");
        addItem(2, "CCTV-2", "This is CCTV-2");
        addItem(3, "CCTV-3", "This is CCTV-3");
        addItem(4, "CCTV-4", "This is CCTV-4");
        addItem(5, "CCTV-5", "This is CCTV-5");
    }
    
    /**
     * @desc 將電視頻道節目添加菜單集合中
     * @param channe  頻道
     * @param name  名稱
     * @param description  描述
     * @return void
     */
    public void addItem(int channe,String name,String description){
        MenuItem tvMenuItem = new MenuItem(channe, name, description);
        menuItems.add(tvMenuItem);
    }

    public List<MenuItem> getMenuItems() {
        return menuItems;
    }

}

      小杏子負責電影菜單模塊的開發。她是使用數組完成的,他認爲數組的效率高些,並且可以控制菜單的長度。測試

public class FilmMenu implements TelevisionMenu{
    static final int MAX_ITEMS = 5;    //菜單最大長度
    MenuItem[] menuItems;
    int numberOfItems = 0;
    
    /**
     * 構造函數完成初始化
     */
    public FilmMenu(){
        menuItems = new MenuItem[MAX_ITEMS];
        
        addItem(1, "絕世天劫", "這是布魯斯威利斯主演的...");
        addItem(2, "達芬奇密碼", "這是我最喜歡的電影之一...");
        addItem(3, "黑客帝國123", "不知道你可以看懂不???");
        addItem(4, "個人女朋友是機器人", "一部我不反感的經典愛情電影...");
        addItem(5, "肖申克的救贖", "自由,幸福,離你有多遠");
    }
    
    /**
     * @desc 將電影解決添加到菜單項中
     * @param channe
     * @param name
     * @param description
     * @return void
     */
    public void addItem(int channe,String name,String description){
        MenuItem tvmenuiItem = new MenuItem(channe, name, description);
        //判斷數組是否越界
        if(numberOfItems > MAX_ITEMS){
            System.out.println("很差意思,菜單滿了....");
        }
        else{
            menuItems[numberOfItems] = tvmenuiItem;
            numberOfItems ++;
        }
    }

    public MenuItem[] getMenuItems() {
        return menuItems;
    }

}

      這裏就只介紹兩個了。他們完成了各自菜單功能的實現,當你在作三個菜單的統一顯示時,你必需要要調用他們的getMenuItems(),來取得他們各個菜單裏面的值,可是這個時候問題出現了,你發現他們的返回值都不相同,要想展現出來你必須這麼幹。優化

List<MenuItem> tvMenuItems = tvMenu.getMenuItems();
        for(int i = 0 ; i < tvMenuItems.size() ; i++){
            MenuItem menuItem = tvMenuItems.get(i);
            ............
        }

        FilmMenu filmMenu = new FilmMenu();
        MenuItem[] filmItems = filmMenu.getMenuItems();
        for(int i = 0 ; i < filmItems.length ; i++){
            MenuItem menuItem = filmItems[i];
            ............
        }

      在這裏咱們老是須要來處理這個兩個菜單,經過循環來遍歷這些菜單選項,若是小安子的實現也不一樣呢?是否是得要三個循環呢?若是下次有須要增長一個模塊呢?在這裏你就惱火了,他們爲何不採用相同的實現方式,可是這個時候你也不可能讓他們去更改了,他們有太多的代碼在依賴着這個兩個菜單了,若是更改就意味着要重寫不少的代碼了。因此你就想是否是能夠來封裝循環呢?不錯就是封裝遍歷。這就是迭代器模式的動機--可以遊走於聚合內的每個元素,同時還能夠提供多種不一樣的遍歷方式ui

1、模式定義

      何謂迭代器模式?所謂迭代器模式就是提供一種方法順序訪問一個聚合對象中的各個元素,而不是暴露其內部的表示。在實際的開發過程當中,咱們可能須要針對不一樣的需求,可能須要以不一樣的方式來遍歷整個整合對象,可是咱們不但願在聚合對象的抽象接口層中充斥着各類不一樣的便利操做這個時候咱們就須要這樣一種東西,它應該具有以下三個功能:this

      一、可以便利一個聚合對象。編碼

      二、咱們不須要了解聚合對象的內部結構。spa

      三、可以提供多種不一樣的遍歷方式。code

      這三個功能就是迭代器模式須要解決的問題。做爲一個功能強大的模式,迭代器模式把在元素之間遊走的責任交給迭代器,而不是聚合對象這樣作就簡化了聚合的接口和實現,也可讓聚合更專一在它所應該專一的事情上,這樣作就更加符合單一責任原則

2、模式結構

      下圖是迭代器模式UML圖。

clip_image001

      從上面能夠看書迭代器模式有以下幾個角色:

      Iterator: 抽象迭代器:全部迭代器都須要實現的接口,提供了遊走聚合對象元素之間的方法。

      ConcreteIterator: 具體迭代器。利用這個具體的迭代器可以對具體的聚合對象進行遍歷。每個聚合對象都應該對應一個具體的迭代器。

      Aggregate: 抽象聚合類。

      ConcreteAggregate: 具體聚合類。實現creatorIterator()方法,返回該聚合對象的迭代器。

3、模式實現

     下面利用迭代器模式對上面案例進行優化整改。下面是正對該實例的UML圖。

clip_image002

      首先咱們須要定義迭代器接口。Iterator.java

public interface Iterator {
    boolean hasNext();
    Object next();
}

      而後是咱們兩個具體的迭代器。一個迭代器遍歷電視界面、一個迭代器遍歷電影界面。

      電影節目的迭代器:FilmMenuIterator.java

public class FilmMenuIterator implements Iterator{
    MenuItem[] menuItems;
    int position = 0;
    
    public FilmMenuIterator(MenuItem[] menuItems){
        this.menuItems = menuItems;
    }
    
    public boolean hasNext() {
        if(position > menuItems.length-1 || menuItems[position] == null){
            return false;
        }
        return true;
    }

    public Object next() {
        MenuItem menuItem = menuItems[position];
        position ++;
        return menuItem;
    }

}

      電視界面的迭代器:TVChanneMenuIterator.java

public class TVChanneMenuIterator implements Iterator{

    List<MenuItem> menuItems;
    int position = 0;
    
    public TVChanneMenuIterator(List<MenuItem> menuItems){
        this.menuItems = menuItems;
    }
    
    public boolean hasNext() {
        if(position > menuItems.size()-1 || menuItems.get(position) == null){
            return false;
        }
        else{
            return true;
        }
    }

    public Object next() {
        MenuItem menuItem = menuItems.get(position);
        position ++;
        return menuItem;
    }
}

      而後是菜單接口,該接口提供返回具體迭代器的方法:createIterator()。

public interface TelevisionMenu {
    public void addItem(int channe,String name,String description);
    public Iterator createIrerator();
}

      兩個具體聚合類。這個兩個聚合類實現createIterator()方法,分別返回電視界面的聚合類和電影界面的聚合類。

public class FilmMenu implements TelevisionMenu{
    static final int MAX_ITEMS = 5;    //菜單最大長度
    MenuItem[] menuItems;
    int numberOfItems = 0;
    
    /**
     * 構造函數完成初始化
     */
    public FilmMenu(){
        menuItems = new MenuItem[MAX_ITEMS];
        
        addItem(1, "絕世天劫", "這是布魯斯威利斯主演的...");
        addItem(2, "達芬奇密碼", "這是我最喜歡的電影之一...");
        addItem(3, "黑客帝國123", "不知道你可以看懂不???");
        addItem(4, "個人女朋友是機器人", "一部我不反感的經典愛情電影...");
        addItem(5, "肖申克的救贖", "自由,幸福,離你有多遠");
    }
    
    /**
     * @desc 將電影解決添加到菜單項中
     * @param channe
     * @param name
     * @param description
     * @return void
     */
    public void addItem(int channe,String name,String description){
        MenuItem tvmenuiItem = new MenuItem(channe, name, description);
        //判斷數組是否越界
        if(numberOfItems > MAX_ITEMS){
            System.out.println("很差意思,菜單滿了....");
        }
        else{
            menuItems[numberOfItems] = tvmenuiItem;
            numberOfItems ++;
        }
    }

    public Iterator createIrerator() {
        return new FilmMenuIterator(menuItems);
    }

}

 

public class TVChanneMenu implements TelevisionMenu{
    List<MenuItem> menuItems;
    
    /**
     * 構造函數完成初始化
     */
    public TVChanneMenu(){
        menuItems = new ArrayList<MenuItem>();
        addItem(1, "CCTV-1", "This is CCTV-1");
        addItem(2, "CCTV-2", "This is CCTV-2");
        addItem(3, "CCTV-3", "This is CCTV-3");
        addItem(4, "CCTV-4", "This is CCTV-4");
        addItem(5, "CCTV-5", "This is CCTV-5");
    }
    
    /**
     * @desc 將電視頻道節目添加菜單集合中
     * @param channe  頻道
     * @param name  名稱
     * @param description  描述
     * @return void
     */
    public void addItem(int channe,String name,String description){
        MenuItem tvMenuItem = new MenuItem(channe, name, description);
        menuItems.add(tvMenuItem);
    }

    public Iterator createIrerator() {
        return new TVChanneMenuIterator(menuItems);
    }
    
}

      終於完成了,如今就能夠來實現主菜單了,用來展現、遍歷全部的電視、電影界面咯。

public class MainMenu {
    TelevisionMenu tvMenu;
    FilmMenu filmMenu;
    
    public MainMenu(TelevisionMenu tvMenu,FilmMenu filmMenu){
        this.tvMenu = tvMenu;
        this.filmMenu  = filmMenu;
    }
    
    public void printMenu(){
        Iterator tvIterator = tvMenu.createIrerator();
        Iterator filmIterator = filmMenu.createIrerator();
        
        System.out.println("電視節目有:");
        printMenu(tvIterator);
        System.out.println("----------------------------------------------------------------");
        System.out.println("電影節目有:");
        printMenu(filmIterator);
    }

    private void printMenu(Iterator iterator) {
        while(iterator.hasNext()){
            MenuItem menuItem = (MenuItem) iterator.next();
            System.out.print("channe:"+menuItem.getChanne()+",  ");
            System.out.print("name:"+menuItem.getName()+",  ");
            System.out.println("description :"+menuItem.getDescription());
        }
    }
}

      測試程序:

public class Test {
    public static void main(String[] args) {
        TVChanneMenu tvMenu = new TVChanneMenu();
        FilmMenu filmMenu = new FilmMenu();
        
        MainMenu mainMenu = new MainMenu(tvMenu, filmMenu);
        mainMenu.printMenu();
    }
}

      運行結果

clip_image003

4、模式優缺點

優勢

      一、它支持以不一樣的方式遍歷一個聚合對象。

      二、迭代器簡化了聚合類。

      三、在同一個聚合上能夠有多個遍歷。

      四、在迭代器模式中,增長新的聚合類和迭代器類都很方便,無須修改原有代碼。

缺點

      因爲迭代器模式將存儲數據和遍歷數據的職責分離,增長新的聚合類須要對應增長新的迭代器類,類的個數成對增長,這在必定程度上增長了系統的複雜性。

5、模式使用場景

      一、訪問一個聚合對象的內容而無須暴露它的內部表示。

      二、須要爲聚合對象提供多種遍歷方式。

      三、爲遍歷不一樣的聚合結構提供一個統一的接口。

6、模式總結

      一、迭代器模式提供一種方法來訪問聚合對象,而不用暴露這個對象的內部表示。

      二、將遍歷聚合對象中數據的行爲提取出來,封裝到一個迭代器中,經過專門的迭代器來遍歷聚合對象的內部數據,這就是迭代器模式的本質。迭代器模式是「單一職責原則」的完美體現。

      三、當使用迭代器的時候,咱們依賴聚合提供遍歷。

      四、迭代器提供了一個通用的接口,讓咱們遍歷聚合的項,放咱們編碼使用聚合項時,就可使用多態機制。

相關文章
相關標籤/搜索