一文徹底搞明白迭代器模式
本篇講解Java設(shè)計(jì)模式中的迭代器模式,分為定義、模式應(yīng)用前案例、結(jié)構(gòu)、模式應(yīng)用后案例、適用場(chǎng)景、模式可能存在的困惑和本質(zhì)探討7個(gè)部分。
定義
迭代器模式提供一種方法順序訪問(wèn)一個(gè)聚合對(duì)象中的各個(gè)元素,而又不需暴露該對(duì)象的內(nèi)部表示。
在新的分類方式中,迭代器模式被劃分至類之間的交互類別中,其簡(jiǎn)化的是調(diào)用方對(duì)一個(gè)或一組對(duì)象遍歷行為的交互。

模式應(yīng)用前案例
在銀行業(yè)務(wù)領(lǐng)域中,銀行包含很多客戶,而一個(gè)客戶又可能包含多個(gè)賬戶。下面以這個(gè)案例進(jìn)行說(shuō)明。先來(lái)看一下未使用迭代器模式之前的代碼實(shí)現(xiàn)。
public class Account {//賬戶類
private final String accountNumber;
private final double balance;
public Account(String accountNumber, double balance) {
this.accountNumber = accountNumber;
this.balance = balance;
    }
// 省略其他屬性和方法
public String getAccountNumber(){
return this.accountNumber;
    }
public double getBalance(){
return this.balance;
    }
}
public class Customer {//客戶類
private final String name;
private final List<Account> accounts;
public Customer(String name) {
this.name = name;
this.accounts = new ArrayList<>();
    }
public void addAccount(Account account) {
this.accounts.add(account);
    }
// 遍歷賬戶信息
public void displayAccounts() {
        System.out.println("Customer: " + this.name);
//for (Account account : this.accounts) {//底層使用Iterator實(shí)現(xiàn)
for(int i=0;i<this.accounts.size();i++) {
            System.out.println("Account Number: " + this.accounts.get(i).getAccountNumber() + ", Balance: " + this.accounts.get(i).getBalance());
        }
    }
}
public class Bank {//銀行集合類
private final List<Customer> customers;
public Bank(){
this.customers = new ArrayList<>();
    }
// 添加顧客
public void addCustomer(Customer customer){
this.customers.add(customer);
    }
// 顯示所有客戶的帳號(hào)信息
public void displayAllCustomersAndAccounts() {
//for (Customer customer : this.customers) {//底層使用Iterator實(shí)現(xiàn)
for(int i=0;i<this.customers.size();i++) {
this.customers.get(i).displayAccounts();
        }
    }
}
public class Client {//調(diào)用方代碼
public static void main(String[] args) {
        Bank bank= new Bank();
        Customer customer1 = new Customer ("Sun");
        customer1.addAccount(new Account( "1234" ,1000.0));
        customer1.addAccount(new Account( "5678",500.0));
        Customer customer2 = new Customer("Wang");
        customer2.addAccount(new Account( "9012" ,200.0));
        customer2.addAccount(new Account( "3456",40000));
        bank.addCustomer(customer1);
        bank.addCustomer(customer2);
        bank.displayAllCustomersAndAccounts();
    }
}對(duì)于迭代器模式,Java語(yǔ)言中的集合已經(jīng)內(nèi)置支持。在上述代碼中,注釋掉的增強(qiáng)的for循環(huán)方式(如for (Account account : this.accounts)),其底層也會(huì)轉(zhuǎn)換成Iterator方式。
因此,主要是對(duì)比f(wàn)or(int i=0;i<size;i++)這種方式和Iterator迭代器方式之間的優(yōu)劣。
結(jié)構(gòu)

迭代器模式的上述結(jié)構(gòu)是一個(gè)通用的結(jié)構(gòu),其代碼實(shí)現(xiàn)如下。
public interface Iterator<T> {
T next();
boolean hasNext();
}
public class ConcreteIterator<T> implements Iterator {
private int currentIndex = 0;
private T[] t;
public ConcreteIterator(T[] t) {
this.t = t;
    }
@Override
public Object next() {
return t[currentIndex++];
    }
@Override
public boolean hasNext() {
return currentIndex < t.length;
    }
}
public interface Aggregate {
Iterator createIterator();
}
public class ConcreteAggregate implements Aggregate {
public String[] items;
public ConcreteAggregate() {
        items = new String[10];
for(int i=0;i<items.length;i++) {
            items[i] = "str"+i;
        }
    }
@Override
public Iterator createIterator() {
return new ConcreteIterator<String>(items);
    }
}
public class Client {
public static void main(String[] args) {
        ConcreteAggregate aggregate = new ConcreteAggregate();
        Iterator<String> iterator =  aggregate.createIterator();
while (iterator.hasNext()) {
            System.out.println(iterator.next());
        }
    }
}模式應(yīng)用后案例
由于Java語(yǔ)言已經(jīng)內(nèi)置迭代器實(shí)現(xiàn)。上面的銀行領(lǐng)域案例,如果應(yīng)用迭代器模式,代碼實(shí)現(xiàn)如下。
public class Account {//賬戶類
private final String accountNumber;
private final double balance;
public Account(String accountNumber, double balance) {
this.accountNumber = accountNumber;
this.balance = balance;
    }
// 省略其他屬性和方法
public String getAccountNumber(){
return this.accountNumber;
    }
public double getBalance(){
return this.balance;
    }
}
public class Customer {//客戶類
private final String name;
private final List<Account> accounts;
public Customer(String name) {
this.name = name;
this.accounts = new ArrayList<>();
    }
public void addAccount(Account account) {
this.accounts.add(account);
    }
public Iterator<Account> iterator() {
return this.accounts.iterator();
    }
public String getName() {
return this.name;
    }
// 遍歷賬戶信息
public void displayAccounts() {
//循環(huán)賬戶
        Iterator<Account> acctIterator = this.iterator();
while(acctIterator.hasNext()){
            Account acount = acctIterator.next();
            System.out.println("Acount Number:"+acount.getAccountNumber() + ", Balance: "+acount.getBalance());
        }
    }
}
public class Bank implements Iterable<Customer>{
private final List<Customer> customers;
public Bank() {
this.customers =new ArrayList<>();
    }
// 添加顧客
public void addCustomer(Customer customer){
this.customers.add(customer);
    }
@Override
public Iterator<Customer> iterator(){
return this.customers.iterator();
    }
// 顯示所有客戶的帳號(hào)信息
public void displayAllCustomersAndAccounts() {
        Iterator<Customer> customerIterator = this.iterator();
//循環(huán)客戶
while(customerIterator.hasNext()) {
            Customer customer = customerIterator.next();
            System.out.println("Customer: " +customer.getName());
            customer.displayAccounts();
        }
    }
}
public class Client {//調(diào)用方代碼
public static void main(String[] args) {
        Bank bank = new Bank();
        Customer customer1 = new Customer("Sun");
        customer1.addAccount(new Account("1234", 1000.0));
        customer1.addAccount(new Account("5678", 500.0));
        Customer customer2= new Customer ("Wang");
        customer2.addAccount(new Account( "9012" ,200.0));
        customer2.addAccount(new Account( "3456",40000));
        bank.addCustomer(customer1);
        bank.addCustomer(customer2);
        bank.displayAllCustomersAndAccounts();
    }
}Java語(yǔ)言中提供了Iterable接口,然后重寫里面的iterator方法。通過(guò)該方法就可以得到一個(gè)Iterator對(duì)象,然后可以利用這個(gè)Iterator對(duì)象就可以依次訪問(wèn)集合中的元素。
適用場(chǎng)景
迭代器模式適用于以下場(chǎng)景:
1、訪問(wèn)一個(gè)聚合對(duì)象的內(nèi)容而無(wú)需暴露它的內(nèi)部表示
2、支持對(duì)聚合對(duì)象的多種遍歷方式,如樹、圖等
3、對(duì)遍歷不同的聚合結(jié)構(gòu)提供一個(gè)統(tǒng)一的接口
模式可能存在的困惑
困惑1:增強(qiáng)for循環(huán)(如for(obj:ObjList))與Iterator迭代器方式有何區(qū)別?
增強(qiáng)for循環(huán)方式相當(dāng)于Java語(yǔ)言中的一種語(yǔ)法糖。在編譯階段,會(huì)轉(zhuǎn)換成Iterator方式實(shí)現(xiàn)。
困惑2:普通for循環(huán)(如for(int i=0;i<size;i++))似乎也比較簡(jiǎn)潔,Iterator相比有什么優(yōu)勢(shì)?
針對(duì)數(shù)組、鏈表等簡(jiǎn)單的數(shù)據(jù)結(jié)構(gòu),兩種循環(huán)方式其實(shí)體現(xiàn)不出優(yōu)勢(shì)。但是,對(duì)于樹和圖等復(fù)雜數(shù)據(jù)結(jié)構(gòu),普通for循環(huán)很難支持。
例如,對(duì)于樹(Tree)這類數(shù)據(jù)結(jié)構(gòu),至少包括以下三種遍歷方式:
1)前序遍歷(Preorder Traversal):先訪問(wèn)根節(jié)點(diǎn),然后遞歸地前序遍歷左子樹和右子樹;
2)中序遍歷(Inorder Traversal):先遞歸地中序遍歷左子樹,然后訪問(wèn)根節(jié)點(diǎn),最后遞歸地中序遍歷右子樹;
3)后序遍歷(Postorder Traversal):先遞歸地后序遍歷左子樹和右子樹,最后訪問(wèn)根節(jié)點(diǎn)。
對(duì)于圖(Graph)這類數(shù)據(jù)結(jié)構(gòu),至少也要支持以下兩種遍歷方式
1)深度優(yōu)先搜索(Depth-First Search, DFS):從起始頂點(diǎn)出發(fā),在走過(guò)一條路徑上所有未被標(biāo)記過(guò)的頂點(diǎn)之前不回退;
2)廣度優(yōu)先搜索(Breadth-First Search, BFS):從起始頂點(diǎn)開始向外層擴(kuò)散搜索,并且按照距離排序依次進(jìn)行探索。
此外,由于迭代器是一個(gè)家族類,最上層是一個(gè)Iterable接口,后續(xù)也可以靈活擴(kuò)展其他更高效的遍歷方式。
本質(zhì)
對(duì)于一個(gè)類來(lái)說(shuō),對(duì)于其屬性或狀態(tài)的遍歷是類的一種行為。但是,這種行為不屬于核心業(yè)務(wù)操作。
因此,迭代器模式的本質(zhì)上是將這種遍歷行為通用化,這樣也可以為調(diào)用方提供統(tǒng)一的訪問(wèn)接口。















 
 
 












 
 
 
 