天天看點

設計模式--行為型模式--疊代器模式

在現實生活以及程式設計中,經常要通路一個聚合對象中的各個元素,如“​​資料結構​​​”中的連結清單周遊,通常的做法是将連結清單的建立和周遊都放在同一個類中,但這種方式不利于程式的擴充,如果要更換周遊方法就必須修改程式源代碼,這違背了 “開閉原則”。

既然将周遊方法封裝在聚合類中不可取,那麼聚合類中不提供周遊方法,将周遊方法由使用者自己實作是否可行呢?答案是同樣不可取,因為這種方式會存在兩個缺點:

  1. 暴露了聚合類的内部表示,使其資料不安全;
  2. 增加了客戶的負擔。

“疊代器模式”能較好地克服以上缺點,它在客戶通路類與聚合類之間插入一個疊代器,這分離了聚合對象與其周遊行為,對客戶也隐藏了其内部細節,且滿足“單一職責原則”和“開閉原則”,如 ​​Java​​​ 中的 Collection、List、Set、Map 等都包含了疊代器。

疊代器模式在生活中應用的比較廣泛,比如:物流系統中的傳送帶,不管傳送的是什麼物品,都會被打包成一個個箱子,并且有一個統一的二維碼。這樣我們不需要關心箱子裡是什麼,在分發時隻需要一個個檢查發送的目的地即可。再比如,我們平時乘坐交通工具,都是統一刷卡或者刷臉進站,而不需要關心是男性還是女性、是殘障人士還是正常人等資訊。

模式的定義與特點

疊代器(Iterator)模式的定義:提供一個對象來順序通路聚合對象中的一系列資料,而不暴露聚合對象的内部表示。疊代器模式是一種對象行為型模式,其主要優點如下。

  1. 通路一個聚合對象的内容而無須暴露它的内部表示。
  2. 周遊任務交由疊代器完成,這簡化了聚合類。
  3. 它支援以不同方式周遊一個聚合,甚至可以自定義疊代器的子類以支援新的周遊。
  4. 增加新的聚合類和疊代器類都很友善,無須修改原有代碼。
  5. 封裝性良好,為周遊不同的聚合結構提供一個統一的接口。

其主要缺點是:增加了類的個數,這在一定程度上增加了系統的複雜性。

在日常開發中,我們幾乎不會自己寫疊代器。除非需要定制一個自己實作的資料結構對應的疊代器,否則,開源架構提供的 API 完全夠用。

模式的結構與實作

疊代器模式是通過将聚合對象的周遊行為分離出來,抽象成疊代器類來實作的,其目的是在不暴露聚合對象的内部結構的情況下,讓外部代碼透明地通路聚合的内部資料。現在我們來分析其基本結構與實作方法。

1. 模式的結構

疊代器模式主要包含以下角色。

  1. 抽象聚合(Aggregate)角色:定義存儲、添加、删除聚合對象以及建立疊代器對象的接口。
  2. 具體聚合(ConcreteAggregate)角色:實作抽象聚合類,傳回一個具體疊代器的執行個體。
  3. 抽象疊代器(Iterator)角色:定義通路和周遊聚合元素的接口,通常包含 hasNext()、first()、next() 等方法。
  4. 具體疊代器(Concretelterator)角色:實作抽象疊代器接口中所定義的方法,完成對聚合對象的周遊,記錄周遊的目前位置。

其結構圖如圖 1 所示。

設計模式--行為型模式--疊代器模式

2. 模式的實作

疊代器模式的實作代碼如下:

package net.biancheng.c.iterator;
import java.util.*;
public class IteratorPattern {
    public static void main(String[] args) {
        Aggregate ag = new ConcreteAggregate();
        ag.add("中山大學");
        ag.add("華南理工");
        ag.add("韶關學院");
        System.out.print("聚合的内容有:");
        Iterator it = ag.getIterator();
        while (it.hasNext()) {
            Object ob = it.next();
            System.out.print(ob.toString() + "\t");
        }
        Object ob = it.first();
        System.out.println("\nFirst:" + ob.toString());
    }
}
//抽象聚合
interface Aggregate {
    public void add(Object obj);
    public void remove(Object obj);
    public Iterator getIterator();
}
//具體聚合
class ConcreteAggregate implements Aggregate {
    private List         list = new ArrayList         ();         public void add(Object obj) {             list.add(obj);         }         public void remove(Object obj) {             list.remove(obj);         }         public Iterator getIterator() {             return (new ConcreteIterator(list));         }     }     //抽象疊代器     interface Iterator {         Object first();         Object next();         boolean hasNext();     }     //具體疊代器     class ConcreteIterator implements Iterator {         private List         list = null;         private int index = -1;         public ConcreteIterator(List         list) {             this.list = list;         }         public boolean hasNext() {             if (index < list.size() - 1) {                 return true;             } else {                 return false;             }         }         public Object first() {             index = 0;             Object obj = list.get(index);             ;             return obj;         }         public Object next() {             Object obj = null;             if (this.hasNext()) {                 obj = list.get(++index);             }             return obj;         }     }                          

程式運作結果如下:

聚合的内容有:中山大學    華南理工    韶關學院   
First:中山大學      

模式的應用執行個體

【例1】用疊代器模式編寫一個浏覽婺源旅遊風景圖的程式。

分析:婺源的名勝古迹較多,要設計一個檢視相關景點圖檔(​​​點此下載下傳本執行個體所要顯示的景點圖檔​​​)和簡介的程式,用“疊代器模式”設計比較合适。

首先,設計一個婺源景點(WyViewSpot)類來儲存每張圖檔的名稱與簡介;再設計一個景點集(ViewSpotSet)接口,它是抽象聚合類,提供了增加和删除婺源景點的方法,以及擷取疊代器的方法。

然後,定義一個婺源景點集(WyViewSpotSet)類,它是具體聚合類,用 ArrayList 來儲存所有景點資訊,并實作父類中的抽象方法;再定義婺源景點的抽象疊代器(ViewSpotltemtor)接口,其中包含了檢視景點資訊的相關方法。

最後,定義婺源景點的具體疊代器(WyViewSpotlterator)類,它實作了父類的抽象方法;用戶端程式設計成視窗程式,它初始化婺源景點集(ViewSpotSet)中的資料,并實作 ActionListener 接口,它通過婺源景點疊代器(ViewSpotlterator)來査看婺源景點(WyViewSpot)的資訊。圖 2 所示是其結構圖。

設計模式--行為型模式--疊代器模式

 程式代碼如下:

package net.biancheng.c.iterator;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
public class PictureIterator {
    public static void main(String[] args) {
        new PictureFrame();
    }
}
//相框類
class PictureFrame extends JFrame implements ActionListener {
    private static final long serialVersionUID = 1L;
    ViewSpotSet ag; //婺源景點集接口
    ViewSpotIterator it; //婺源景點疊代器接口
    WyViewSpot ob;    //婺源景點類
    PictureFrame() {
        super("中國最美鄉村“婺源”的部分風景圖");
        this.setResizable(false);
        ag = new WyViewSpotSet();
        ag.add(new WyViewSpot("江灣", "江灣景區是婺源的一個國家5A級旅遊景區,景區内有蕭江宗祠、永思街、滕家老屋、婺源人家、鄉賢園、百工坊等一大批古建築,精美絕倫,做工精細。"));
        ag.add(new WyViewSpot("李坑", "李坑村是一個以李姓聚居為主的古村落,是國家4A級旅遊景區,其建築風格獨特,是著名的徽派建築,給人一種安靜、祥和的感覺。"));
        ag.add(new WyViewSpot("思溪延村", "思溪延村位于婺源縣思口鎮境内,始建于南宋慶元五年(1199年),當時建村者俞氏以(魚)思清溪水而名。"));
        ag.add(new WyViewSpot("曉起村", "曉起有“中國茶文化第一村”與“國家級生态示範村”之美譽,村屋多為清代建築,風格各具特色,村中小巷均鋪青石,曲曲折折,回環如棋局。"));
        ag.add(new WyViewSpot("菊徑村", "菊徑村形狀為山環水繞型,小河成大半圓型,繞村莊将近一周,四周為高山環繞,符合中國的八卦“後山前水”設計,當地人稱“臉盆村”。"));
        ag.add(new WyViewSpot("篁嶺", "篁嶺是著名的“曬秋”文化起源地,也是一座距今近六百曆史的徽州古村;篁嶺屬典型山居村落,民居圍繞水口呈扇形梯狀錯落排布。"));
        ag.add(new WyViewSpot("彩虹橋", "彩虹橋是婺源頗有特色的帶頂的橋——廊橋,其不僅造型優美,而且它可在雨天裡供行人歇腳,其名取自唐詩“兩水夾明鏡,雙橋落彩虹”。"));
        ag.add(new WyViewSpot("卧龍谷", "卧龍谷是國家4A級旅遊區,這裡飛泉瀑流洩銀吐玉、彩池幽潭碧綠清新、山峰岩石挺拔奇巧,活脫脫一幅天然潑墨山水畫。"));
        it = ag.getIterator(); //擷取婺源景點疊代器
        ob = it.first();
        this.showPicture(ob.getName(), ob.getIntroduce());
    }
    //顯示圖檔
    void showPicture(String Name, String Introduce) {
        Container cp = this.getContentPane();
        JPanel picturePanel = new JPanel();
        JPanel controlPanel = new JPanel();
        String FileName = "src/iterator/Picture/" + Name + ".jpg";
        JLabel lb = new JLabel(Name, new ImageIcon(FileName), JLabel.CENTER);
        JTextArea ta = new JTextArea(Introduce);
        lb.setHorizontalTextPosition(JLabel.CENTER);
        lb.setVerticalTextPosition(JLabel.TOP);
        lb.setFont(new Font("宋體", Font.BOLD, 20));
        ta.setLineWrap(true);
        ta.setEditable(false);
        //ta.setBackground(Color.orange);
        picturePanel.setLayout(new BorderLayout(5, 5));
        picturePanel.add("Center", lb);
        picturePanel.add("South", ta);
        JButton first, last, next, previous;
        first = new JButton("第一張");
        next = new JButton("下一張");
        previous = new JButton("上一張");
        last = new JButton("最末張");
        first.addActionListener(this);
        next.addActionListener(this);
        previous.addActionListener(this);
        last.addActionListener(this);
        controlPanel.add(first);
        controlPanel.add(next);
        controlPanel.add(previous);
        controlPanel.add(last);
        cp.add("Center", picturePanel);
        cp.add("South", controlPanel);
        this.setSize(630, 550);
        this.setVisible(true);
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }
    @Override
    public void actionPerformed(ActionEvent arg0) {
        String command = arg0.getActionCommand();
        if (command.equals("第一張")) {
            ob = it.first();
            this.showPicture(ob.getName(), ob.getIntroduce());
        } else if (command.equals("下一張")) {
            ob = it.next();
            this.showPicture(ob.getName(), ob.getIntroduce());
        } else if (command.equals("上一張")) {
            ob = it.previous();
            this.showPicture(ob.getName(), ob.getIntroduce());
        } else if (command.equals("最末張")) {
            ob = it.last();
            this.showPicture(ob.getName(), ob.getIntroduce());
        }
    }
}
//婺源景點類
class WyViewSpot {
    private String Name;
    private String Introduce;
    WyViewSpot(String Name, String Introduce) {
        this.Name = Name;
        this.Introduce = Introduce;
    }
    public String getName() {
        return Name;
    }
    public String getIntroduce() {
        return Introduce;
    }
}
//抽象聚合:婺源景點集接口
interface ViewSpotSet {
    void add(WyViewSpot obj);
    void remove(WyViewSpot obj);
    ViewSpotIterator getIterator();
}
//具體聚合:婺源景點集
class WyViewSpotSet implements ViewSpotSet {
    private ArrayList list = new ArrayList();
    public void add(WyViewSpot obj) {
        list.add(obj);
    }
    public void remove(WyViewSpot obj) {
        list.remove(obj);
    }
    public ViewSpotIterator getIterator() {
        return (new WyViewSpotIterator(list));
    }
}
//抽象疊代器:婺源景點疊代器接口
interface ViewSpotIterator {
    boolean hasNext();
    WyViewSpot first();
    WyViewSpot next();
    WyViewSpot previous();
    WyViewSpot last();
}
//具體疊代器:婺源景點疊代器
class WyViewSpotIterator implements ViewSpotIterator {
    private ArrayList list = null;
    private int index = -1;
    WyViewSpot obj = null;
    public WyViewSpotIterator(ArrayList list) {
        this.list = list;
    }
    public boolean hasNext() {
        if (index < list.size() - 1) {
            return true;
        } else {
            return false;
        }
    }
    public WyViewSpot first() {
        index = 0;
        obj = list.get(index);
        return obj;
    }
    public WyViewSpot next() {
        if (this.hasNext()) {
            obj = list.get(++index);
        }
        return obj;
    }
    public WyViewSpot previous() {
        if (index > 0) {
            obj = list.get(--index);
        }
        return obj;
    }
    public WyViewSpot last() {
        index = list.size() - 1;
        obj = list.get(index);
        return obj;
    }
}      

程式運作結果如圖 3 所示。

設計模式--行為型模式--疊代器模式

模式的應用場景

前面介紹了關于疊代器模式的結構與特點,下面介紹其應用場景,疊代器模式通常在以下幾種情況使用。

  1. 當需要為聚合對象提供多種周遊方式時。
  2. 當需要為周遊不同的聚合結構提供一個統一的接口時。
  3. 當通路一個聚合對象的内容而無須暴露其内部細節的表示時。

由于聚合與疊代器的關系非常密切,是以大多數語言在實作聚合類時都提供了疊代器類,是以大數情況下使用語言中已有的聚合類的疊代器就已經夠了。

模式的擴充

疊代器模式常常與​​組合模式​​結合起來使用,在對組合模式中的容器構件進行通路時,經常将疊代器潛藏在組合模式的容器構成類中。當然,也可以構造一個外部疊代器來對容器構件進行通路,其結構圖如圖 4 所示。

設計模式--行為型模式--疊代器模式