軟體設計模式學習(二十)迭代器模式

来源:https://www.cnblogs.com/Yee-Q/archive/2020/05/25/12957360.html
-Advertisement-
Play Games

迭代器模式是一種使用頻率非常高的設計模式,迭代器用於對一個聚合對象進行遍歷。通過引入迭代器可以將數據的遍歷功能從聚合對象中分離出來,聚合對象只負責存儲數據,聚合對象只負責存儲數據,而遍曆數據由迭代器來完成。 模式動機 一個聚合對象,如一個列表(List)或者一個集合(Set),應該提供一種方法來讓別 ...



迭代器模式是一種使用頻率非常高的設計模式,迭代器用於對一個聚合對象進行遍歷。通過引入迭代器可以將數據的遍歷功能從聚合對象中分離出來,聚合對象只負責存儲數據,聚合對象只負責存儲數據,而遍曆數據由迭代器來完成。


模式動機

一個聚合對象,如一個列表(List)或者一個集合(Set),應該提供一種方法來讓別人可以訪問它的元素,而又不需要暴露它的內部結構。此外,針對不同的需要,可能還要以不同方式遍歷整個聚合對象,但是我們不希在聚合對象的抽象層介面中充斥著各種不同遍歷的操作。怎樣遍歷一個聚合對象,又不需要瞭解聚合對象的內部結構,還能提供多種不同的遍歷方式,這就是迭代器模式所要解決的問題。

迭代器模式中,提供一個外部的迭代器來對聚合對象進行訪問和遍歷,迭代器定義一個訪問該聚合元素的介面,並且可以跟蹤當前遍歷對象,瞭解哪些元素已經遍歷過而哪些沒有。


模式定義

提供一種方法來訪問聚合對象,而不用暴露這個對象的內部表示,其別名為游標(Cursor)。迭代器模式是一種對象行為模式。


模式結構

  1. Iterator(抽象迭代器)

    抽象迭代器定義了訪問和遍歷元素的介面,一般聲明以下方法:

    • 用於獲取第一個元素的 first()
    • 用於訪問下一個元素的 next()
    • 用於判斷是否還有下一個元素的 hasNext()
    • 用於獲取當前元素的 currentItem()
  2. ConcreteIterator(具體迭代器)

    具體迭代器實現了抽象迭代器介面,完成對聚合對象的遍歷,同時在對聚合進行遍歷時跟蹤其當前位置

  3. Aggregate(抽象聚合類)

    抽象聚合類用於存儲對象,並定義創建相應迭代器對象的介面,聲明一個 createIterator() 方法用於創建一個迭代器對象

  4. ConcreteAggregate(具體聚合類)

    具體聚合類實現了創建相應迭代器的介面,實現了在聚合類中聲明的 createIterator() 方法,該方法返回一個與具體聚合對應的具體迭代器 ConcreteIterator 實例


模式分析

存儲數據是聚合對象的最基本職責,其中包含存儲數據的類型、存儲空間的大小、存儲空間的分配,以及存儲的方式和順序。然而,聚合對象除了能存儲數據外,還必須提供遍歷訪問其內部數據的方式,同時這些遍歷方式可能會根據不同的情形提供不同的實現。

因此,聚合對象主要有兩個職責:一是存儲內部數據;二是遍歷內部數據。前者是聚合對象的基本功能,後者是可以分離的。根據單一職責原則,對象承擔的職責越少,對象的穩定性就越好,我們將遍歷聚合對象中數據的行為提取出來,封裝到一個迭代器中,通過專門的迭代器來遍歷聚合對象的內部數據。迭代器模式是單一職責原則的完美體現。

下麵通過一個簡單的自定義迭代器來分析迭代器模式的結構

首先定義一個簡單的迭代器去介面

public interface MyIterator {
    void first();	// 訪問第一個元素
    void next();	// 訪問下一個元素
    boolean isLast();	// 判斷是否是最後一個元素
    Object currentItem();	// 獲取當前元素
}

然後需要定義一個聚合介面

public interface MyCollection {
    // 返回一個 MyIterator 迭代器對象
    MyIterator createIterator();
}

定義好抽象層之後,我們需要定義抽象迭代器介面和抽象聚合介面的實現類,一般將具體迭代器類作為具體聚合類的內部類,從而迭代器可以實現直接訪問聚合類中的數據

public class NewCollection implements MyCollection {
    
    private Object[] obj = {"dog", "pig", "cat", "monkey", "pig"};
    
    public MyIterator createIterator() {
        return new NewIterator();
    }
    
    private class NewIterator implements MyIterator {
        
        private int currentIndex = 0;
        
        public void first() {
            currentIndex = 0;
        }
        
        public void next() {
            if(currentIndex < obj.length) {
                currentIndex++;
            }
        }
        
        public boolean isLast() {
            return currentIndex == obj.length;
        }
        
        public void currentItem() {
            return obj[currentIndex];
        }
    }     
}

NewCollection 類實現了 MyCollection 介面,實現了 createIterator() 方法,同時定義了一個數組用於存儲數據元素,還定義了一個實現了 MyIterator 介面的內部類,索引變數 currentIndex 用於保存所操作的數組元素的下標值。客戶端代碼如下:

public class Client {
    
    public static void process(MyCollection collection) {
        MyIterator i = collection.createIterator();
        while(!i.isLast()) {
            System.out.println(i.currentItem().toString());
            i.next();
        }
    }
    
    public static void main(String args[]) {
        MyCollection collection = new NewCollection();
        process(collection);
    }
}

除了使用內部類實現之外,也可以使用常規的方式來實現迭代器

public class ConcreteIterator implements Iterator {
    
    private ConcreteAggregate objects;
    
    public ConcreteIterator(ConcreteAggregate objects) {
        this.objects = objects;
    }
    
    public void first() {
        ...
    }
        
    public void next() {
        ...
    }
        
    public boolean isLast() {
        ...
    }
        
    public void currentItem() {
        ...
    }
}

public class ConcreteAggregate implements Aggregate {
    ...
    public Iterator createIterator() {
        return new ConcreteIterator(this);
    }
}

迭代器模式中應用了工廠方法模式,聚合類充當工廠類,而迭代器充當產品類


模式優缺點

迭代器模式優點:

  1. 支持以不同的方式遍歷一個聚合對象。在迭代器模式中只需要用一個不同的迭代器來替換原有迭代器即可改變遍歷演算法,也可以自己定義迭代器的子類以支持新的遍歷方式。
  2. 迭代器簡化了聚合類。原有聚合對象不再需要自行提供遍曆數據等操作方法。
  3. 在同一個聚合上可以有多個遍歷。由於每個迭代器都保持自己的遍歷狀態,因此可以對一個聚合對象進行多個遍歷操作。
  4. 增加新的聚合類和迭代器類都很方便,無須修改原代碼,滿足開閉原則。

迭代器模式缺點:

  1. 由於迭代器模式將存儲數據和遍曆數據的職責分離,增加新的聚合類需要對應增加新的迭代器類,類的個數成對增加,在一定程度上增加了系統的複雜性

模式適用環境

在以下情況可以使用迭代器模式:

  1. 訪問一個聚合對象的內容而無須暴露它的內部表示
  2. 需要為聚合對象提供多種遍歷方式
  3. 為遍歷不同的聚合結構提供一個統一的介面

Java 迭代器

Java 中的集合框架 Collections,其基本介面層次結構如圖

Collection 是所有集合類的根介面,它的主要方法如下:

boolean add(Object c);
boolean addAll(Collection c);
boolean remove(Object o);
boolean removeAll(Collection c);
boolean remainAll(Collection c);
Iterator iterator();

Collection 的 iterator() 方法返回一個 java.util.Iterator 類型的對象,而其子介面 java.util.List 的 listIterator() 方法返回一個 java.util.ListIterator 類型的對象,ListIterator 是 Iterator 的子類,它們構成了 Java 語言對迭代器模式的支持。

在 JDK 中,Iterator 介面具有如下三個基本方法:

  1. Object next():通過反覆調用 next() 方法可以逐個訪問聚合中的元素
  2. boolean hasNext():用於判斷聚合對象在是否還存在下一個元素,為了不拋出異常,必須在調用 next() 之前先調用 hasNext()。如果迭代對象仍然擁有可供訪問的元素,那麼 hasNext() 返回 true
  3. void remove():刪除上一次調用 next() 時返回的元素

Java 迭代器可以理解為它工作時在聚合對象的各個元素之間,每調用一次 next() 方法,迭代器便越過下個元素,並且返回它剛越過的那個元素的地址引用。

在第一個 next() 方法被調用時,迭代器由“元素1”與“元素2”之間移動至“元素2”與“元素3”之間,跨越了“元素2”,因此 next() 方法將返回對“元素2”的引用;在第二個 next() 方法被調用時,迭代器由“元素2”與“元素3”之間移至“元素3”與“元素4”之間,next() 方法將返回對“元素3”的引用,此時調用 remove() 方法,則可將”元素3“刪除。

需要註意的是,next() 方法與 remove() 方法的調用是相互關聯的。如果調用 remove() 之前沒有先對 next() 進行調用,那麼將拋出異常,因為沒有任何可供刪除的元素



您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 在我們開發Vue應用的時候,很多時候需要記錄一些變數的內容,這些可以用來做界面狀態的承載,也可以作為頁面間交換數據的處理,處理這些內容可以歸為Vuex的狀態控制。例如我們往往前端需要訪問後端數據,一般是通過封裝的Web API調用獲取數據,而使用Store模式來處理相關的數據或者狀態的變化,而視圖V... ...
  • # 概述 - 1.新增的屬性 placeholder Calendar, date, time, email, url, search ContentEditable Draggable Hidden Context-menu Data-Val(自定義屬性) - 2.新增的標簽 語義化標簽(一群類似 ...
  • 因人而異 自學肯定也是可以的,最主要還是要看個人的學習能力,意志力,和自己的決心, 下麵我就說一下,在自學時需要註意的一些誤區和需要掌握哪些技術知識,才能去找工作。 前端自學者存在的學習誤區: 1、所學東西可能已過時 奉為經典的東西可能已經過時,或者已經有了更好的替代者,而你獲取信息的渠道有限,消息 ...
  • 隨著技術以如此快的速度發展,現在我們有必要選擇合適的工具來使用。每個軟體項目都有它需要滿足的多個需求和規範,因此為了滿足這些需求,選擇一種編程語言以允許您以有效的方式開發和管理項目非常重要。 由於有許多種編程語言和框架可供選擇,它們之間的比較已成為必然,因為你需要知道哪一個提供了最好的服務。當涉及到 ...
  • 什麼是NodeJS JS是腳本語言,腳本語言都需要一個解析器才能運行。對於寫在HTML頁面里的JS,瀏覽器充當瞭解析器的角色。而對於需要獨立運行的JS,NodeJS就是一個解析器。 每一種解析器都是一個運行環境,不但允許JS定義各種數據結構,進行各種計算,還允許JS使用運行環境提供的內置對象和方法做 ...
  • 之前一直採用VS進行各種前端後端的開發,隨著項目的需要,正逐步融合純前端的開發模式,開始主要選型為Vue + Element 進行BS前端的開發,後續會進一步整合Vue + AntDesign的界面套件,作為兩種不同界面框架的展現方式。採用Vue + Element 的前端開發和之前的開發模式需要有... ...
  • 從事web前端6年的工作,曾經是信息管理的一名應屆生,由於專業難找工作,掙錢少,當時選擇了轉行學前端開發技術,今天師兄就給大家講一下,作為應屆生,想學前端快點找工作應該如何去學。 對於畢業生來說,最要緊的事情就是快點找到工作。所以你學前端的時候就抓重點來學,因為很多東西,工作上用不到,所以學了也沒必 ...
  • 前提: (1) 相關博文地址: SpringBoot + Vue + ElementUI 實現後臺管理系統模板 -- 前端篇(一):搭建基本環境:https://www.cnblogs.com/l-y-h/p/12930895.html SpringBoot + Vue + ElementUI 實現 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...