一文彻底搞明白迭代器模式

开发 前端
对于一个类来说,对于其属性或状态的遍历是类的一种行为。但是,这种行为不属于核心业务操作。因此,迭代器模式的本质上是将这种遍历行为通用化,这样也可以为调用方提供统一的访问接口。​

本篇讲解Java设计模式中的迭代器模式,分为定义、模式应用前案例、结构、模式应用后案例、适用场景、模式可能存在的困惑和本质探讨7个部分。

定义

迭代器模式提供一种方法顺序访问一个聚合对象中的各个元素,而又不需暴露该对象的内部表示。

在新的分类方式中,迭代器模式被划分至类之间的交互类别中,其简化的是调用方对一个或一组对象遍历行为的交互。

模式应用前案例

在银行业务领域中,银行包含很多客户,而一个客户又可能包含多个账户。下面以这个案例进行说明。先来看一下未使用迭代器模式之前的代码实现。

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实现
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);
    }

// 显示所有客户的帐号信息
public void displayAllCustomersAndAccounts() {
//for (Customer customer : this.customers) {//底层使用Iterator实现
for(int i=0;i<this.customers.size();i++) {
this.customers.get(i).displayAccounts();
        }
    }
}

public class Client {//调用方代码

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语言中的集合已经内置支持。在上述代码中,注释掉的增强的for循环方式(如for (Account account : this.accounts)),其底层也会转换成Iterator方式。

因此,主要是对比for(int i=0;i<size;i++)这种方式和Iterator迭代器方式之间的优劣。

结构

迭代器模式的上述结构是一个通用的结构,其代码实现如下。

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());
        }
    }
}

模式应用后案例

由于Java语言已经内置迭代器实现。上面的银行领域案例,如果应用迭代器模式,代码实现如下。

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() {
//循环账户
        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();
    }

// 显示所有客户的帐号信息
public void displayAllCustomersAndAccounts() {
        Iterator<Customer> customerIterator = this.iterator();
//循环客户
while(customerIterator.hasNext()) {
            Customer customer = customerIterator.next();
            System.out.println("Customer: " +customer.getName());

            customer.displayAccounts();
        }
    }
}

public class Client {//调用方代码

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语言中提供了Iterable接口,然后重写里面的iterator方法。通过该方法就可以得到一个Iterator对象,然后可以利用这个Iterator对象就可以依次访问集合中的元素。

适用场景

迭代器模式适用于以下场景:

1、访问一个聚合对象的内容而无需暴露它的内部表示

2、支持对聚合对象的多种遍历方式,如树、图等

3、对遍历不同的聚合结构提供一个统一的接口

模式可能存在的困惑

困惑1:增强for循环(如for(obj:ObjList))与Iterator迭代器方式有何区别?

增强for循环方式相当于Java语言中的一种语法糖。在编译阶段,会转换成Iterator方式实现。

困惑2:普通for循环(如for(int i=0;i<size;i++))似乎也比较简洁,Iterator相比有什么优势?

针对数组、链表等简单的数据结构,两种循环方式其实体现不出优势。但是,对于树和图等复杂数据结构,普通for循环很难支持。

例如,对于树(Tree)这类数据结构,至少包括以下三种遍历方式:

1)前序遍历(Preorder Traversal):先访问根节点,然后递归地前序遍历左子树和右子树;

2)中序遍历(Inorder Traversal):先递归地中序遍历左子树,然后访问根节点,最后递归地中序遍历右子树;

3)后序遍历(Postorder Traversal):先递归地后序遍历左子树和右子树,最后访问根节点。

对于图(Graph)这类数据结构,至少也要支持以下两种遍历方式

1)深度优先搜索(Depth-First Search, DFS):从起始顶点出发,在走过一条路径上所有未被标记过的顶点之前不回退;

2)广度优先搜索(Breadth-First Search, BFS):从起始顶点开始向外层扩散搜索,并且按照距离排序依次进行探索。

此外,由于迭代器是一个家族类,最上层是一个Iterable接口,后续也可以灵活扩展其他更高效的遍历方式。

本质

对于一个类来说,对于其属性或状态的遍历是类的一种行为。但是,这种行为不属于核心业务操作。

因此,迭代器模式的本质上是将这种遍历行为通用化,这样也可以为调用方提供统一的访问接口。

责任编辑:武晓燕 来源: 今日头条
相关推荐

2024-05-09 09:09:19

组合模式对象

2024-05-13 10:45:25

中介模式面向对象数量

2024-05-10 08:43:04

外观模式接口系统

2024-05-17 10:08:59

享元模式分类方式

2024-05-15 17:41:37

备忘录模式多线程

2019-08-27 14:46:59

ElasticSearES数据库

2020-07-10 08:03:35

DNS网络ARPAne

2023-05-29 08:45:45

Java注解数据形式

2023-03-13 08:12:37

Golang编程路径问题

2019-10-28 10:19:27

JVM 类加载器Java

2019-09-09 11:02:17

Nginx进程模型

2021-05-06 08:03:07

IPIP网络模式calicok8s

2023-11-06 09:06:54

分布式一致性数据

2024-02-23 12:11:53

装饰器模式对象

2022-05-30 10:37:35

分布式事务反向补偿

2021-06-30 08:45:02

内存管理面试

2022-06-07 10:13:22

前端沙箱对象

2020-03-18 14:00:47

MySQL分区数据库

2023-10-27 08:15:45

2020-12-07 06:19:50

监控前端用户
点赞
收藏

51CTO技术栈公众号