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

来源: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
  • 1. 說明 /* Performs operations on System.String instances that contain file or directory path information. These operations are performed in a cross-pla ...
  • 視頻地址:【WebApi+Vue3從0到1搭建《許可權管理系統》系列視頻:搭建JWT系統鑒權-嗶哩嗶哩】 https://b23.tv/R6cOcDO qq群:801913255 一、在appsettings.json中設置鑒權屬性 /*jwt鑒權*/ "JwtSetting": { "Issuer" ...
  • 引言 集成測試可在包含應用支持基礎結構(如資料庫、文件系統和網路)的級別上確保應用組件功能正常。 ASP.NET Core 通過將單元測試框架與測試 Web 主機和記憶體中測試伺服器結合使用來支持集成測試。 簡介 集成測試與單元測試相比,能夠在更廣泛的級別上評估應用的組件,確認多個組件一起工作以生成預 ...
  • 在.NET Emit編程中,我們探討了運算操作指令的重要性和應用。這些指令包括各種數學運算、位操作和比較操作,能夠在動態生成的代碼中實現對數據的處理和操作。通過這些指令,開發人員可以靈活地進行算術運算、邏輯運算和比較操作,從而實現各種複雜的演算法和邏輯......本篇之後,將進入第七部分:實戰項目 ...
  • 前言 多表頭表格是一個常見的業務需求,然而WPF中卻沒有預設實現這個功能,得益於WPF強大的控制項模板設計,我們可以通過修改控制項模板的方式自己實現它。 一、需求分析 下圖為一個典型的統計表格,統計1-12月的數據。 此時我們有一個需求,需要將月份按季度劃分,以便能夠直觀地看到季度統計數據,以下為該需求 ...
  • 如何將 ASP.NET Core MVC 項目的視圖分離到另一個項目 在當下這個年代 SPA 已是主流,人們早已忘記了 MVC 以及 Razor 的故事。但是在某些場景下 SSR 還是有意想不到效果。比如某些靜態頁面,比如追求首屏載入速度的時候。最近在項目中回歸傳統效果還是不錯。 有的時候我們希望將 ...
  • System.AggregateException: 發生一個或多個錯誤。 > Microsoft.WebTools.Shared.Exceptions.WebToolsException: 生成失敗。檢查輸出視窗瞭解更多詳細信息。 內部異常堆棧跟蹤的結尾 > (內部異常 #0) Microsoft ...
  • 引言 在上一章節我們實戰了在Asp.Net Core中的項目實戰,這一章節講解一下如何測試Asp.Net Core的中間件。 TestServer 還記得我們在集成測試中提供的TestServer嗎? TestServer 是由 Microsoft.AspNetCore.TestHost 包提供的。 ...
  • 在發現結果為真的WHEN子句時,CASE表達式的真假值判斷會終止,剩餘的WHEN子句會被忽略: CASE WHEN col_1 IN ('a', 'b') THEN '第一' WHEN col_1 IN ('a') THEN '第二' ELSE '其他' END 註意: 統一各分支返回的數據類型. ...
  • 在C#編程世界中,語法的精妙之處往往體現在那些看似微小卻極具影響力的符號與結構之中。其中,“_ =” 這一組合突然出現還真不知道什麼意思。本文將深入剖析“_ =” 的含義、工作原理及其在實際編程中的廣泛應用,揭示其作為C#語法奇兵的重要角色。 一、下劃線 _:神秘的棄元符號 下劃線 _ 在C#中並非 ...