迭代器模式
前言
很早之前,我們的電視調節頻道是需要用電視上的按鈕去控制的,那時並沒有遙控器,如果我們想要調臺,只能一次又一次的擰按鈕。
越來越高級的電視機相繼出現,現在的電話機,我們有了電視遙控器,我們使用電視遙控器來調臺,這個時候,無需直接操作電視。
我們可以將電視機看成一個存儲電視頻道的集合對象,通過遙控器可以對電視機中的電視頻道集合進行操作,如返回上一個頻道、跳轉到下一個頻道或者跳轉至指定的頻道。遙控器爲我們操作電視頻道帶來很大的方便,用戶並不需要知道這些頻道到底如何存儲在電視機中。
具體案例:
編寫程序展示一個學校院系結構:需求是這樣,要在一個頁面中展示出學校的院系 組成,一個學校有多個學院,一個學院有多個系。分析:
每一個學院都有添加系的功能,如果我們將遍歷的方法hasNext() next()等寫入。這將導致聚合類的職責過重,它既負責存儲和管理數據,又負責遍歷數據,違反了“單一職責原則”,由於聚合類非常龐大,實現代碼過長,還將給測試和維護增加難度。
那麼這個時候,我們也許會這樣想,因爲有多個學院,我們不妨將學院封裝爲接口,但是在這個接口中充斥着大量方法,不利於子類實現,違反了“接口隔離原則”。
解決方案:
解決方案之一就是將聚合類中負責遍歷數據的方法提取出來,封裝到專門的類中,實現數據存儲和數據遍歷分離,無須暴露聚合類的內部屬性即可對其進行操作,而這正是迭代器模式的意圖所在。
基本介紹
- 迭代器模式(Iterator Pattern)是常用的設計模式,屬於行爲型模式
- 如果我們的集合元素是用不同的方式實現的,有數組,還有java的集合類,
或者還有其他方式,當客戶端要遍歷這些集合元素的時候就要使用多種遍歷
方式,而且還會暴露元素的內部結構,可以考慮使用迭代器模式解決。 - 迭代器模式,提供一種遍歷集合元素的統一接口,用一致的方法遍歷集合元素,
不需要知道集合對象的底層表示,即:不暴露其內部的結構。
原理類圖:
角色分析:
- Iterator (抽象迭代器): 迭代器接口,是系統提供,含義 hasNext, next, remove
- ConcreteIterator (具體迭代器): 具體的迭代器類,管理迭代 在具體迭代器中通過遊標來記錄在聚合對象中所處的當前位置,在具體實現時,遊標通常是一個表示位置的非負整數。
- Aggregate (抽象聚合類):一個統一的聚合接口, 將客戶端和具體聚合解耦
- ConcreteAggreage (具體聚合類): 具體的聚合持有對象集合, 並提供一個方法,返回一個
代碼實例:
聚合類:
學院接口:
/**
* 學院
*
* @author 孫一鳴 on 2020/2/17
*/
public interface College {
public String getName();
//增加系的方法
public void addDepartment (String name,String des);
//換回一個迭代器,遍歷
public Iterator createIterator();
}
計算機學院:
/**
* 計算機學院
*
* @author 孫一鳴 on 2020/2/17
*/
public class ComputerCollege implements College
{
Department[] departments;
int numOfDepartment = 0;//保存當前數組對象個數
public ComputerCollege() {
departments = new Department[5];//計算機學院有5個系
addDepartment("Java","JAVA牛逼");
addDepartment("Php","經典");
addDepartment("C#","還好");
addDepartment("Python","智能");
addDepartment("GO","牛");
}
@Override
public String getName() {
return "計算機學院";
}
//給計算機學院添加系
@Override
public void addDepartment(String name,String des) {
Department department = new Department(name,des);
departments[numOfDepartment] = department;
numOfDepartment += 1;
}
@Override
public Iterator createIterator() {
return new ComputerCollegeIterator(departments);
}
}
信息學院
/**
* 信息學院
*
* @author 孫一鳴 on 2020/2/17
*/
public class InfoCollege implements College
{
List<Department> departments;
// int numOfDepartment = 0;//保存當前數組對象個數
public InfoCollege() {
departments = new ArrayList<Department>();
//計算機學院有5個系
addDepartment("信息安全","JAVA牛逼");
addDepartment("網絡安全","經典");
addDepartment("服務器安全","還好");
addDepartment("數據庫安全","智能");
}
@Override
public String getName() {
return "計算機學院";
}
//給計算機學院添加系
@Override
public void addDepartment(String name,String des) {
Department department = new Department(name,des);
departments.add(department);
//numOfDepartment += 1;
}
@Override
public Iterator createIterator() {
return new InfoCollegeIterator(departments);
}
}
學院下面的系:
/**
* 系
*學校 --學院 -- 系
* @author 孫一鳴 on 2020/2/17
*/
public class Department {
private String name;
private String des;
public Department(String name, String des) {
this.name = name;
this.des = des;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDes() {
return des;
}
public void setDes(String des) {
this.des = des;
}
}
迭代器類:
計算機學院迭代器
/**
* @author 孫一鳴 on 2020/2/17
* ComputerCollegeIterator: 計算機學院迭代器,
* 遍歷計算機學院下的系
*/
public class ComputerCollegeIterator implements Iterator {
/*
* 這裏需要知道 Department 以怎樣的方式存放
*
* */
Department[] departments;
int position = 0;//遍歷的位置
public ComputerCollegeIterator(Department[] departments) {
this.departments = departments;
}
@Override
public boolean hasNext() {
if (position >= departments.length || departments[position] ==null)
{
return false;
}else {
return true;
}
}
@Override
public Object next() {
Department department =departments[position];
position +=1;
return department;
}
@Override
public void remove() {
}
}
信息學院迭代器:
/**
* @author 孫一鳴 on 2020/2/17
* ComputerCollegeIterator: 信息學院迭代器,
* 遍歷信息學院下的系
*/
public class InfoCollegeIterator implements Iterator {
/*
* 這裏需要知道 Department 以怎樣的方式存放
*
* */
List<Department> departments;
int position = -1;//索引
public InfoCollegeIterator( List<Department> departments) {
this.departments = departments;
}
@Override
public boolean hasNext() {
if (position >= departments.size()-1 )
{
return false;
}else {
position +=1;
return true;
}
}
@Override
public Object next() {
return departments.get(position);
}
@Override
public void remove() {
}
}
輸出信息類:
/**
* @author 孫一鳴 on 2020/2/17
* 輸出信息類
*/
public class OutPutClass {
List<College> colleges;
public OutPutClass(List<College> colleges) {
this.colleges = colleges;
}
//遍歷單個學院取出系
public void printDepartment(Iterator iterator) {
while (iterator.hasNext()) {
Department department = (Department) iterator.next();
System.out.println("department.getName() = " + department.getName());
}
}
//遍歷學院集合 輸出所有學院
public void printCollege() {
//此時遍歷不用傳入 迭代器
//學院使用List集合 已經實現Iterator
Iterator<College> iterator = colleges.iterator();
while(iterator.hasNext()){
//取出一個學院
College college = iterator.next();
System.out.println("========="+college.getName()+"====");
printDepartment(college.createIterator());
}
}
}
測試類:
/**
* @author 孫一鳴 on 2020/2/17
*/
public class Client {
public static void main(String[] args) {
List<College> colleges = new ArrayList<College>();
College computerCollege = new ComputerCollege();
College infocollege = new InfoCollege();
colleges.add(computerCollege);
colleges.add(infocollege);
OutPutClass outPutClass = new OutPutClass(colleges);
outPutClass.printCollege();
}
}
結果截圖:
代碼分析:
如果需要增加一個新的具體聚合類,只需增加一個新的聚合子類和一個新的具體迭代器類即可,原有類庫代碼無須修改,符合“開閉原則”;
如果需要爲聚合類更換一個迭代器,只需要增加一個新的具體迭代器類作爲抽象迭代器類的子類,重新實現遍歷方法,原有迭代器代碼無須修改,也符合“開閉原則”;
但是如果要在迭代器中增加新的方法,則需要修改抽象迭代器源代碼,這將違背“開閉原則”。
迭代器模式在JDK-ArrayList集合應用的源碼分析
角色分析說明
- 內部類Itr 充當具體實現迭代器Iterator 的類, 作爲ArrayList 內部類
- List 就是充當了聚合接口,含有一個iterator() 方法,返回一個迭代器對象
- ArrayList 是實現聚合接口List 的子類,實現了iterator()
- Iterator 接口系統提供
- 迭代器模式解決了 不同集合(ArrayList ,LinkedList) 統一遍歷問題
優點
- 提供一個統一的方法遍歷對象,客戶不用再考慮聚合的類型,使用一種方法就可以遍歷對象了。
- 隱藏了聚合的內部結構,客戶端要遍歷聚合的時候只能取到迭代器,而不會知道聚合的具體組成。
- 提供了一種設計思想,就是一個類應該只有一個引起變化的原因(叫做單一責任
原則)。在聚合類中,我們把迭代器分開,就是要把管理對象集合和遍歷對象集
合的責任分開,這樣一來集合改變的話,隻影響到聚合對象。而如果遍歷方式改變的話,隻影響到了迭代器。 - 當要展示一組相似對象,或者遍歷一組相同對象時使用, 適合使用迭代器模式
缺點
每個聚合對象都要一個迭代器,會生成多個迭代器不好管理類