迭代器模式 Iterator 行為型 設計模式(二十)

来源:https://www.cnblogs.com/noteless/archive/2018/12/13/10112965.html
-Advertisement-
Play Games

迭代器模式,Iterator,java集合框架內置的一種模式,本文介紹了迭代器模式的起源含義,設計意圖,以及結構形態,並且給出了Java版本的迭代器模式的實現,迭代器模式分為內部迭代和外部迭代,Java集合框架使用的這種形式是比較好的一種方式。 ...


迭代器模式(Iterator) image_5c11cddc_d4   走遍天下,世界那麼大,我想去看看   image_5c11cddc_5f0c 在電腦中,Iterator意為迭代器,迭代有重覆的含義,在程式中,更有“遍歷”的含義 如果給定一個數組,我們可以通過for迴圈來遍歷這個數組,這種遍歷就叫做迭代 對於數組這種數據結構,我們稱為是可迭代的 所以 迭代器就是可以用來對於一個數據集合進行遍歷的對象

意圖

提供一種方法,順序訪問一個聚合對象中的各個元素,而又不需要暴露該對象的內部表示。 別名:游標 Cursor

集合與遍歷

由一個或多個確定的元素所構成的整體叫做集合 多個對象聚集在一起形成的總體稱之為聚集aggregate。 集合和聚集有相似的含義。 容器是指盛物品的器具,Java的Collection框架,就是設計用來保存對象的容器 容器可以認為是集合、聚集的具體體現形式,三個元素在一起叫做集合(聚集),怎麼在一起?數組,列表?這具體的體現形式就是容器   容器必須提供內部對象的訪問方式,如果不能獲取對象,容器也失去了存在的意義,一個只能進不能出的儲蓄罐你要它何用? 因為容器的存在就是為了更方便的使用、管理對象。 而且通常需要容器提供對於內部所有元素的遍歷方法   然而容器其內部有不同的擺放形式,可順序,可無序堆集 簡言之,就是不同類型的容器必然有不同的內部數據結構 那麼,一種解決辦法就是不同的容器各自提供自己的遍歷方法 這樣的話,對於使用容器管理對象的客戶端程式來說: 如果迭代的邏輯,也就是業務邏輯沒有變化 當需要更換為另外的集合時,就需要同時更換這個迭代方法 考慮這樣一個場景
有一個方法,
方法的參數類型為 Collection
他的迭代邏輯,也就是業務邏輯為遍歷所有元素,讀取每個元素的信息,並且進行列印...
如果不同的容器有不同的遍歷方法,也就是一種實現類有一種不同的遍歷方法
一旦更換實現類,那麼就需要同步更換掉這個迭代方法,否則方法將無法通過編譯
  如果集合的實現不變,需要改變業務邏輯,也就是迭代的邏輯 那麼就需要修改容器類的迭代方法,也就是修改原來的遍歷方法   還是上面的場景 有一個方法,方法的參數類型為 Collection 他的迭代邏輯,也就是業務邏輯為遍歷所有元素,讀取每個元素的信息,並且進行列印... 現在他的實現類無需變化 但是業務邏輯需要變動,比如希望從後往前的方式進行遍歷,而不再是從前往後 就需要修改原來的方法或者重新寫一個方法     出現上述問題的根本原因就在於元素的迭代邏輯與容器本身耦合在一起 當迭代邏輯或者集合實現發生變更時,需要進行修改,不符合開閉原則 容器自身不僅僅需要存儲管理對象,還要負責對象的遍歷訪問,不符合單一職責原則   存儲管理對象是容器的核心職責,雖然經常需要提供遍歷方法,但是他並不是核心職責 但是為了提供遍歷元素的方法,可能不得不在容器類內提供各種全局變數,比如保存當前的位置等,這無疑也會導致容器聚集類設計的複雜度

結構

image_5c11cddc_779f 抽象迭代器角色Iterator 定義遍歷元素所需要的介面 具體的迭代器ConcreteIterator 實現了Iterator介面,並且跟蹤當前位置 抽象集合容器角色Aggregate 定義創建相應迭代器的介面(方法) 就是一個容器類,並且定義了一個返回迭代器的方法 具體的容器角色ConcreteAggregate Aggregate的子類,並且實現了創建Iterator對象的介面,也就是返回一個ConcreteIterator實例 客戶端角色Client 持有容器對象以及迭代器對象的引用,調用迭代對象的迭代方法遍歷元素   迭代器模式中,通過一個外部的迭代器來對容器集合對象進行遍歷。 迭代器定義了遍歷訪問元素的協議方式。 容器集合對象提供創建迭代器的方法。

示例代碼

Aggregate角色 提供了iterator()獲取Iterator
package iterator;
public abstract class Aggregate {
abstract Iterator iterator();
abstract Object get(int index);
abstract int size();
}
ConcreateAggregate角色 內部使用一個Object數組,數組直接通過構造方法傳遞進去(只是為了演示學習模式,不要糾結這算不上一個容器) 提供了大小的獲取方法以及獲取指定下標元素的方法 尤其是實現了iterator()方法,創建一個ConcreteIterator實例,將當前ConcreteAggregate作為參數傳遞給他的構造方法
package iterator;
public class ConcreateAggregate extends Aggregate {
 
private Object[] objects;
 
ConcreateAggregate(Object[] objects) {
this.objects = objects;
}
 
@Override
Iterator iterator() {
return new ConcreateIterator(this);
}
 
@Override
Object get(int index) {
return objects[index];
}
 
@Override
int size() {
return objects.length;
}
}
 
迭代器介面 一個是否還有元素的方法,一個獲取下一個元素的方法
package iterator;
public interface Iterator {
boolean hasNext();
Object next();
}
具體的迭代器 內部維護了數據的大小和當前位置 如果下標未到最後,那麼就是還有元素 next()方法用於獲取當前元素,獲取後當前位置往後移動一下
package iterator;
public class ConcreateIterator implements Iterator {
private Aggregate aggregate;
private int index = 0;
private int size = 0;
 
ConcreateIterator(Aggregate aggregate) {
this.aggregate = aggregate;
size = aggregate.size();
}
 
@Override
public boolean hasNext() {
return index < size ? true : false;
}
 
@Override
public Object next() {
Object value = aggregate.get(index);
index++;
return value;
}
}
測試類
package iterator;
public class Client {
public static void main(String[] args) {
Object[] objects = {"1", 2, 3, 4, 5};
Aggregate aggregate = new ConcreateAggregate(objects);
Iterator iterator = aggregate.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
}
}
image_5c11cddc_5524 示例代碼中ConcreateAggregate本身提供了獲取指定下標元素的方法,可以直接調用獲取元素 藉助於Iterator,將迭代邏輯從Aggregate中剝離出來,獨立封裝實現 在客戶端與容器之間,增加了一層Iterator,實現了客戶端程式與容器的解耦 image_5c11cddc_440b 說白了,增加了Iterator,相當於通過Iterator封裝了真實容器對象的獲取元素的方法 不直接調用方法,經過Iterator轉換一層   而且仔細品味下,這有點“適配”的韻味,適配的目標就是統一的元素訪問協議,通過Iterator約定 而被適配的角色,則是真實容器對象元素的操作方法 總之“間接”“委托”“代理”的感覺,對吧,好處自己品味

外部迭代與內部迭代

在上面的示常式序中,通過引入Iterator,實現了迭代邏輯的封裝抽象  但是容器聚集對象本身有獲取元素的方法,所以客戶端仍舊可以自行遍歷 Iterator也只不過是容器聚集對象的一個客戶而已 這種迭代器也叫做外部迭代器 對於外部迭代器有一個問題,對於不同的ConcreteAggregate,可能都需要一個不同的ConcreteIterator 也就是很可能會不得不創建了一個與Aggregate等級結構平行的Iterator結構,出現了很多的ConcreteIterator類 這勢必會增加維護成本 而且,雖然迭代器將客戶端的訪問與容器進行解耦,但是迭代器卻是必須依賴容器對象 也就是迭代器類ConcreteIterator與ConcreteAggregate必須進行通信,會增加設計的複雜度,而且這也會增加類之間的耦合性   另外的一種方法是使用內部類的形式,也就是將ConcreteIterator的實現,移入到ConcreteAggregate的內部 藉助於內部類的優勢:對外部類有充足的訪問許可權,也就是無需擔心為了通信要增加複雜度的問題 準確的說,你沒有任何的通信成本,內部類可以直接讀取外部類的屬性數據信息 而且,使用內部類的方式不會導致類的爆炸(儘管仍舊是會有另一個class文件,但是從代碼維護的角度看算是一個類) 這種形式可以叫做內部迭代器   不過無論哪種方式,你可以看得出來,使用迭代器的客戶端代碼,都是一樣的 藉助於工廠方法iterator()獲得一個迭代器實例(簡單工廠模式) 然後藉助於迭代器進行元素遍歷

JDK中的迭代

我們看下JDK中的Collection提供給我們的迭代方式 Collection是所有集合的父類,Collection實現了Iterable介面 Iterable介面提供了iterator()方法用於返回一個Iterator類的一個實例對象 Iterator類提供了對元素的遍歷方法 image_5c11cddc_295f 接下來看下ArrayList的實現 ArrayList中iterator()返回了一個Itr對象,而這個對象是ArrayList的內部類,實現了Iterator介面 image_5c11cddc_26d 看得出來,java給集合框架內置了迭代器模式 在ArrayList中使用就是內部類的形式,也就是內部迭代器 boolean hasNext() 是否擁有更多元素,換句話說,如果next()方法不會拋出異常,就會返回true next(); 返回下一個元素 remove() 刪除元素   有幾點需要註意 1.)初始時,可以認為“當前位置”為第一個元素前面 所以next()獲取第一個元素 image_5c11cddc_3d19 2.)根據第一點,初始的當前位置”為第一個元素前面,所以如果想要刪除第一個元素的話,必須先next,然後remove
Iterator iterator = list.iterator();

iterator.next();

iterator.remove();

 

否則,會拋出異常 image_5c11cddc_6e70 3.)不僅僅是刪除第一個元素需要先next,然後才能remove,每一個remove,前面必須有一個next,成對出現 所以remove是刪除當前元素 如果下麵這樣,會拋出異常
iterator.next();

iterator.remove();

iterator.remove();

 

image_5c11cddc_424a 4.)迭代器只能遍歷一次,如果需要重新遍歷,可以重新獲取迭代器對象 如果已經遍歷到尾部之後仍舊繼續使用,將會拋出異常
Iterator iterator = list.iterator();
while (iterator.hasNext()) {
iterator.next();
}
iterator.next();
image_5c11cddc_7a48

總結

在java中萬事萬物都是對象 前面的命令模式將請求轉換為命令對象 解釋器模式中,將語法規則轉換為終結符表達式和非終結符表達式 迭代器模式中,將“遍歷元素”轉換為對象   通過迭代器模式引入迭代器,將遍歷邏輯功能從容器聚集對象中分離出來 聚合對象本身只負責數據存儲,遍歷的職責交給了迭代器   對於同一個容器對象,可以定義多種迭代器,也就是可以定義多種遍歷方式 如果需要使用另外的迭代方式,僅僅需要更改迭代器對象即可 這樣你甚至可以把ConcreteIterator使用配置文件進行註入,靈活設置    將迭代遍歷的邏輯從容器對象中分離,必然會減少容器類的複雜程度   當增加新的容器類或者迭代器類時,不需要修改原有的代碼,符合開閉原則   如果你想要將容器聚集對象的遍歷邏輯從容器對象中的分離 或者想要提供多種不同形式的遍歷方式時,或者你想為不同的容器對象提供一致性的遍歷介面邏輯 你就應該考慮迭代器模式了 迭代器模式的應用是如此廣泛,以至於java已經將他內置到集合框架中了 所以對於我們自己來說,多數時候可以認為迭代器模式幾乎用不到了 因為絕大多數時候,使用框架提供的應該就足夠了 在java實現中,迭代器模式的比較好的做法就是Java集合框架使用的這種形式---內部類形式的內部迭代器,如果真的需要自己搞一個迭代器,建議仿照集合框架搞吧   藉助於迭代器模式,如果迭代的邏輯不變,更換另外的集合實現,因為實現了共同的迭代器介面,所以不需要對迭代這塊,無需做任何變動 如果需要改變迭代邏輯,必須增加新的迭代形式,只需要增加一個新的內部類實現迭代器介面即可,其他使用的地方只需要做很小的調整 ArrayList中的ListIterator<E> listIterator() 方法就是如此 有人覺得增加一個類和一個方法這不也是修改麽?個人認為:開閉原則儘管最高境界是完全的對擴展開放對修改關閉,但是也不能死摳字眼 增加了一個新的獲取迭代對象的方法以及一個新的類,總比將原有的源代碼中添加新的方法那種修改要強得多,所有的遍歷邏輯都封裝在新的迭代器實現類中,某種程度上可以認為並沒有“修改源代碼” 使用內部類的形式,有人覺得不還是在一個文件中麽?但是內部類會有單獨的class文件,而且,內部類就像一道牆,分割了內外,所有的邏輯被封裝在迭代器實現類中 不需要影響容器自身的設計實現,所以也是符合單一職責原則的。 原文地址:迭代器模式 Iterator 行為型 設計模式(二十)
您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 一、路由進階Egg路由的路由重定向,路由分組 在router.js修改為如下格式require引用 新建 routers文件夾,分別建admin.js index.js api.js放置不同內容,寫法和原來的路由文件寫法一致。 路由重定向 在Controller中使用 在routers文件中使用 二 ...
  • 這裡是修真院前端小課堂,每篇分享文從 【背景介紹】【知識剖析】【常見問題】【解決方案】【編碼實戰】【擴展思考】【更多討論】【參考文獻】 八個方面深度解析前端知識/技能,本篇分享的是: 【post提交的數據有哪幾種編碼格式?能否通過URL參數獲取用戶賬戶密碼】 1.背景介紹 HTTP/1.1 協議規定 ...
  • 這裡是修真院前端小課堂,每篇分享文從 【背景介紹】【知識剖析】【常見問題】【解決方案】【編碼實戰】【擴展思考】【更多討論】【參考文獻】 八個方面深度解析前端知識/技能,本篇分享的是: 【常見的DOM操作有哪些】 1.背景介紹 什麼是DOM?簡單地說,DOM是一套對文檔的內容進行抽象和概念化的方法 在 ...
  • 這裡是修真院前端小課堂,每篇分享文從 【背景介紹】【知識剖析】【常見問題】【解決方案】【編碼實戰】【擴展思考】【更多討論】【參考文獻】 八個方面深度解析前端知識/技能,本篇分享的是: 【px、em、rem、%、vw、wh、vm等單位有什麼區別?】 1.背景介紹傳統的項目開發中,我們只會用到px、%、 ...
  • 最近在IE10下運行一個以前的做web系統發現了兩個小問題: 一、圖片上使用"alt"屬性來添加一些文字提示信息在IE10下無法正常顯示出來 上網查了一下原因:原來是現在一些較新的瀏覽器,比如在IE9、IE10使用"alt"屬性時,在圖片能夠正常顯示的情況下,在圖片中設置"alt"屬性的值將不會顯示 ...
  • 聲明 本篇內容梳理自以下幾個來源: "Github:smyhvae/web" "Bootstrap網站的 less 文檔" "Sass中文網" 感謝大佬們的分享。 正文 CSS預處理(less&Sass) CSS預處理 什麼是 CSS 預處理?為什麼要有 CSS 預處理? 這裡就講講這兩個問題,寫過 ...
  • 來源:http://www.fhadmin.org/webnewsdetail3.html 前臺:支持(5+1[時尚單頁風格])六套模版,可以在後臺切換 官網:www.fhadmin.org 系統介紹: 1.網站後臺採用主流的 SSM 框架 jsp JSTL,網站後臺採用freemaker靜態化模版 ...
  • 作者按:《每天一個設計模式》旨在初步領會設計模式的精髓,目前採用 和`python`兩種語言實現。誠然,每種設計模式都有多種實現方式,但此小冊只記錄最直截了當的實現方式 :) 原文地址是: "《每天一個設計模式之命令模式》" 歡迎關註個人技術博客: "godbmw.com" 。每周 1 篇原創技術分 ...
一周排行
    -Advertisement-
    Play Games
  • 示例項目結構 在 Visual Studio 中創建一個 WinForms 應用程式後,項目結構如下所示: MyWinFormsApp/ │ ├───Properties/ │ └───Settings.settings │ ├───bin/ │ ├───Debug/ │ └───Release/ ...
  • [STAThread] 特性用於需要與 COM 組件交互的應用程式,尤其是依賴單線程模型(如 Windows Forms 應用程式)的組件。在 STA 模式下,線程擁有自己的消息迴圈,這對於處理用戶界面和某些 COM 組件是必要的。 [STAThread] static void Main(stri ...
  • 在WinForm中使用全局異常捕獲處理 在WinForm應用程式中,全局異常捕獲是確保程式穩定性的關鍵。通過在Program類的Main方法中設置全局異常處理,可以有效地捕獲並處理未預見的異常,從而避免程式崩潰。 註冊全局異常事件 [STAThread] static void Main() { / ...
  • 前言 給大家推薦一款開源的 Winform 控制項庫,可以幫助我們開發更加美觀、漂亮的 WinForm 界面。 項目介紹 SunnyUI.NET 是一個基於 .NET Framework 4.0+、.NET 6、.NET 7 和 .NET 8 的 WinForm 開源控制項庫,同時也提供了工具類庫、擴展 ...
  • 說明 該文章是屬於OverallAuth2.0系列文章,每周更新一篇該系列文章(從0到1完成系統開發)。 該系統文章,我會儘量說的非常詳細,做到不管新手、老手都能看懂。 說明:OverallAuth2.0 是一個簡單、易懂、功能強大的許可權+可視化流程管理系統。 有興趣的朋友,請關註我吧(*^▽^*) ...
  • 一、下載安裝 1.下載git 必須先下載並安裝git,再TortoiseGit下載安裝 git安裝參考教程:https://blog.csdn.net/mukes/article/details/115693833 2.TortoiseGit下載與安裝 TortoiseGit,Git客戶端,32/6 ...
  • 前言 在項目開發過程中,理解數據結構和演算法如同掌握蓋房子的秘訣。演算法不僅能幫助我們編寫高效、優質的代碼,還能解決項目中遇到的各種難題。 給大家推薦一個支持C#的開源免費、新手友好的數據結構與演算法入門教程:Hello演算法。 項目介紹 《Hello Algo》是一本開源免費、新手友好的數據結構與演算法入門 ...
  • 1.生成單個Proto.bat內容 @rem Copyright 2016, Google Inc. @rem All rights reserved. @rem @rem Redistribution and use in source and binary forms, with or with ...
  • 一:背景 1. 講故事 前段時間有位朋友找到我,說他的窗體程式在客戶這邊出現了卡死,讓我幫忙看下怎麼回事?dump也生成了,既然有dump了那就上 windbg 分析吧。 二:WinDbg 分析 1. 為什麼會卡死 窗體程式的卡死,入口門檻很低,後續往下分析就不一定了,不管怎麼說先用 !clrsta ...
  • 前言 人工智慧時代,人臉識別技術已成為安全驗證、身份識別和用戶交互的關鍵工具。 給大家推薦一款.NET 開源提供了強大的人臉識別 API,工具不僅易於集成,還具備高效處理能力。 本文將介紹一款如何利用這些API,為我們的項目添加智能識別的亮點。 項目介紹 GitHub 上擁有 1.2k 星標的 C# ...