迭代器設計模式知識概括

GeorgeLin~發表於2020-10-13

迭代器設計模式簡介

學校院系展示需求:

  • 編寫程式展示一個學校院系結構:需求是這樣,要在一個頁面中展示出學校的院系組成,一個學校有多個學院,一個學院有多個系。 如圖:
    在這裡插入圖片描述

傳統的方式的問題分析:

  • 將學院看做是學校的子類,系是學院的子類,這樣實際上是站在組織大小來進行分層次的
  • 實際上我們的要求是 : 在一個頁面中展示出學校的院系組成,一個學校有多個學院,一個學院有多個系, 因此這種方案,不能很好實現的遍歷的操作
  • 假如計算機學院的院系儲存在陣列中,資訊工程學院的院系儲存在集合中,我們要怎麼才能定義一個統一的規範,來遍歷不同儲存結構下的院系。解決方案: => 迭代器模式在這裡插入圖片描述

迭代器模式基本介紹:

  • 迭代器模式(Iterator Pattern) 是常用的設計模式,屬於行為型模式
  • 如果我們的集合元素是用不同的方式實現的,有陣列,還有java的集合類,或者還有其他方式,當客戶端要遍歷這些集合元素的時候就要使用多種遍歷方式,而且還會暴露元素的內部結構,可以考慮使用迭代器模式解決。
  • 迭代器模式,提供一種遍歷集合元素的統一介面,用一致的方法遍歷集合元素,對於使用者來說,不需要知道集合物件的底層表示,即:不暴露集合的內部結構

迭代器模式的原理類圖:

  • Iterator :迭代器介面,由 JDK提供,該介面定義了三個用於遍歷集合(陣列)的方法:hasNext()、next()、remove()
  • ConcreteIterator:具體的迭代器類,實現具體的迭代邏輯
  • Aggregate:一個統一的聚合介面,將客戶端和具體的 Aggregate 實現類解耦
  • ConcreteAggreage :具體的聚合實現類,該類持有物件集合(Element),並提供一個方法,返回一個迭代器, 該迭代器可以正確遍歷集合
  • Client:客戶端,通過 Iterator 和 Aggregate 依賴其對應的子類在這裡插入圖片描述

迭代器模式解決學校院系展示需求

簡介:

  • 應用例項要求:編寫程式展示一個學校院系結構: 需求是這樣, 要在一個頁面中展示出學校的院系組成, 一個學校有多個學院,一個學院有多個系
  • 類圖:在這裡插入圖片描述
  • 程式碼實現:

Department:實體類,用於表示學院的系

//系
public class Department {

	private String name;
	private String desc;

	public Department(String name, String desc) {
		this.name = name;
		this.desc = desc;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getDesc() {
		return desc;
	}

	public void setDesc(String desc) {
		this.desc = desc;
	}

}

ComputerCollegeIterator:計算機院系的迭代器,該類實現了 Iterator 介面,用於迭代計算機學院的系

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() {

	}

}

InfoColleageIterator:資訊院系的迭代器,該類實現了 Iterator 介面,用於迭代資訊學院的系

public class InfoColleageIterator implements Iterator {

	List<Department> departmentList; // 資訊工程學院是以List方式存放系
	int index = -1;// 索引

	public InfoColleageIterator(List<Department> departmentList) {
		this.departmentList = departmentList;
	}

	// 判斷list中還有沒有下一個元素
	@Override
	public boolean hasNext() {
		if (index >= departmentList.size() - 1) {
			return false;
		} else {
			index += 1;
			return true;
		}
	}

	@Override
	public Object next() {
		return departmentList.get(index);
	}

	// 空實現remove
	@Override
	public void remove() {

	}

}

College:學院的聚合介面

public interface College {

	public String getName();

	// 增加系的方法
	public void addDepartment(String name, String desc);

	// 返回一個迭代器,遍歷
	public Iterator createIterator();
	
}

ComputerCollege:計算機學院的聚合介面實現類,該類聚合了 Department[] 陣列,並提供訪問 Department[] 陣列的迭代器

public class ComputerCollege implements College {

	Department[] departments;
	int numOfDepartment = 0;// 儲存當前陣列的物件個數

	public ComputerCollege() {
		departments = new Department[5];
		addDepartment("Java專業", " Java專業 ");
		addDepartment("PHP專業", " PHP專業 ");
		addDepartment("大資料專業", " 大資料專業 ");
		numOfDepartment = 3;
	}

	@Override
	public String getName() {
		return "計算機學院";
	}

	@Override
	public void addDepartment(String name, String desc) {
		Department department = new Department(name, desc);
		departments[numOfDepartment] = department;
		numOfDepartment += 1;
	}

	@Override
	public Iterator createIterator() {
		return new ComputerCollegeIterator(departments);
	}

}

InfoCollege:資訊學院的聚合介面實現類,該類聚合了 List 集合,並提供訪問 List 集合的迭代器

public class InfoCollege implements College {

	List<Department> departmentList;

	public InfoCollege() {
		departmentList = new ArrayList<Department>();
		addDepartment("資訊保安專業", " 資訊保安專業 ");
		addDepartment("網路安全專業", " 網路安全專業 ");
		addDepartment("伺服器安全專業", " 伺服器安全專業 ");
	}

	@Override
	public String getName() {
		return "資訊工程學院";
	}

	@Override
	public void addDepartment(String name, String desc) {
		Department department = new Department(name, desc);
		departmentList.add(department);
	}

	@Override
	public Iterator createIterator() {
		return new InfoColleageIterator(departmentList);
	}

}

OutPutImpl:為 Client 層提供遍歷輸出學院系的方法

public class OutPutImpl {

	// 學院集合
	List<College> collegeList;

	public OutPutImpl(List<College> collegeList) {
		this.collegeList = collegeList;
	}

	// 遍歷所有學院,然後呼叫printDepartment 輸出各個學院的系
	public void printCollege() {
		// 從collegeList 取出所有學院, Java 中的 List 已經實現Iterator
		Iterator<College> iterator = collegeList.iterator();

		while (iterator.hasNext()) {
			// 取出一個學院
			College college = iterator.next();
			System.out.println("=== " + college.getName() + "=====");
			printDepartment(college.createIterator()); // 得到對應迭代器
		}
	}

	// 輸出學院的系
	public void printDepartment(Iterator iterator) {
		while (iterator.hasNext()) {
			Department d = (Department) iterator.next();
			System.out.println(d.getName());
		}
	}

}

Client:測試程式碼

public class Client {

	public static void main(String[] args) {
		// 建立學院
		List<College> collegeList = new ArrayList<College>();

		ComputerCollege computerCollege = new ComputerCollege();
		InfoCollege infoCollege = new InfoCollege();

		collegeList.add(computerCollege);
		collegeList.add(infoCollege);

		OutPutImpl outPutImpl = new OutPutImpl(collegeList);
		outPutImpl.printCollege();
	}

}

總結:

  • 院系資料真實存放在 ComputerCollege 和 InfoCollege 中,如果想要遍歷院系,需要將院系作為引數傳入ComputerCollegeIterator 和 InfoColleageIterator 中,獲得迭代器,進行迭代

JDK ArrayList 原始碼分析

原理類圖分析:

  • ArrayList 的內部類 Itr 充當了具體實現迭代器 Iterator 的類
  • List 就是充當了聚合介面, 含有一個 iterator() 方法, 該方法返回一個迭代器物件
  • ArrayList 是實現聚合介面 List 的子類, 實現了 iterator() 方法
  • Iterator 介面系統(JDK)提供
  • 迭代器模式解決了不同集合(ArrayList,LinkedList) 統一遍歷問題在這裡插入圖片描述

原始碼追蹤:

Iterator 介面中定義的方法

public interface Iterator<E> {
    
    boolean hasNext();

    E next();

    default void remove() {
        throw new UnsupportedOperationException("remove");
    }

    default void forEachRemaining(Consumer<? super E> action) {
        Objects.requireNonNull(action);
        while (hasNext())
            action.accept(next());
    }
    
}

在 List 介面中定義了獲取 iterator 的抽象方法,即 List 充當了聚合介面

public interface List<E> extends Collection<E> {
    
    // ...
    
    Iterator<E> iterator();
    
    // ...

在 ArrayList 中實現了 iterator 方法

public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable
    
    // ...
    
    public Iterator<E> iterator() {
        return new Itr();
    }    
   
    // ...

Itr 實現了 Iterator 介面,為 ArrayList 的內部類,在 Itr 中,直接使用 ArrayList 中用於存放資料的 Object[] 陣列,通過 size 屬性標識集合元素個數

private class Itr implements Iterator<E> {
    int cursor;       // index of next element to return
    int lastRet = -1; // index of last element returned; -1 if no such
    int expectedModCount = modCount;

    public boolean hasNext() {
        return cursor != size;
    }

    @SuppressWarnings("unchecked")
    public E next() {
        checkForComodification();
        int i = cursor;
        if (i >= size)
            throw new NoSuchElementException();
        Object[] elementData = ArrayList.this.elementData;
        if (i >= elementData.length)
            throw new ConcurrentModificationException();
        cursor = i + 1;
        return (E) elementData[lastRet = i];
    }

    public void remove() {
        if (lastRet < 0)
            throw new IllegalStateException();
        checkForComodification();

        try {
            ArrayList.this.remove(lastRet);
            cursor = lastRet;
            lastRet = -1;
            expectedModCount = modCount;
        } catch (IndexOutOfBoundsException ex) {
            throw new ConcurrentModificationException();
        }
    }

    @Override
    @SuppressWarnings("unchecked")
    public void forEachRemaining(Consumer<? super E> consumer) {
        Objects.requireNonNull(consumer);
        final int size = ArrayList.this.size;
        int i = cursor;
        if (i >= size) {
            return;
        }
        final Object[] elementData = ArrayList.this.elementData;
        if (i >= elementData.length) {
            throw new ConcurrentModificationException();
        }
        while (i != size && modCount == expectedModCount) {
            consumer.accept((E) elementData[i++]);
        }
        // update once at end of iteration to reduce heap write traffic
        cursor = i;
        lastRet = i - 1;
        checkForComodification();
    }

    final void checkForComodification() {
        if (modCount != expectedModCount)
            throw new ConcurrentModificationException();
    }
}

迭代器模式的注意事項

迭代器模式的注意事項和細節:

  • 優點:
    ①提供一個統一的方法遍歷物件,客戶不用再考慮聚合的型別,使用一種方法就可以遍歷物件了。
    ②隱藏了聚合的內部結構,客戶端要遍歷聚合的時候只能取到迭代器,而不會知道聚合的具體組成。
    ③提供了一種設計思想,就是一個類應該只有一個引起變化的原因(叫做單一責任原則)。在聚合類中,我們把迭代器分開,就是要把管理物件集合和遍歷物件集合的責任分開,這樣一來集合改變的話,隻影響到聚合物件。而如果遍歷方式改變的話,隻影響到了迭代器。
    ④當要展示一組相似物件,或者遍歷一組相同物件時使用,適合使用迭代器模式
  • 缺點:
    ①每個聚合物件都要一個迭代器,會生成多個迭代器不好管理類

相關文章