《一天一模式》— 迭代器模式

1、迭代器模式的概念

供一种方法顺序访问一个聚合对象中的各类元素,而又不暴露该对象的内部表示。 java

2、何时使用迭代器模式

迭代器模式使用的场景很明确,那就是封装遍历数组

当业务要对你的对象中的某一数据结构进行遍历,一般有两种作法:安全

  • 把数据结构返回给调用者,让他去遍历;
  • 引用迭代器模式,封装遍历;

下面具体来看看怎么作。bash

3、怎么使用迭代器模式

经过一个业务场景进行说明,仍是使用Java代码作演示。数据结构

假设有一个两种菜单(系统菜单和业务菜单),需求是须要遍历这两种菜单。先来看看不使用迭代器模式有什么弊端。ide

3.1 不适用迭代器模式带来的弊端

加入先开发了业务菜单,数据结构是数组:函数

@Data
public class BusinessMenu {

    private String[] menus = new String[3];

    public BusinessMenu() {
        menus[0] = "业务菜单A";
        menus[1] = "业务菜单B";
        menus[2] = "业务菜单C";
    }

}

而后开发了系统菜单,这是数据结构换成了集合:工具

@Data
public class SystemMenu {

    private List<String> menus = new ArrayList<>();

    public SystemMenu() {
        menus.add("系统菜单1");
        menus.add("系统菜单2");
        menus.add("系统菜单3");
    }

}

而后实现遍历的客户端以下:spa

public class Client {

    public static void main(String[] args) {
        SystemMenu menus = new SystemMenu();
        List<String> mlist = menus.getMenus();
        printSystemMenu(mlist);
        System.out.println();
        BusinessMenu buttons = new BusinessMenu();
        String[] blist = buttons.getMenus();
        printBusinessMenu(blist);
    }

    public static void printSystemMenu(List<String> mlist) {
        for (int i = 0; i < mlist.size(); i++) {
            System.out.println(mlist.get(i));
        }
    }

    public static void printBusinessMenu(String[] blist) {
        for (int i = 0; i < blist.length; i++) {
            System.out.println(blist[i]);
        }
    }

}

这种状况就是第二节说到的,把数据结构返回给调用者,让他去实现遍历,说的更确切一些,是让他去操做数据结构进行遍历。设计

这种作法有两个弊端:

  • 数据安全:若是没有很好的控制数据结构的访问权限,不能限制调用者,有可能会对数据形成破坏;
  • 扩展性低:上面的代码写了两个函数,printSystemMenu和printBusinessMenu,由于内部数据结构不一样,因此实现不一样,若是再有其余的Menu对象,内部的数据结构不是数据或者List,那么又要添加新的遍历方法,这不复合开闭原则,也是扩展性低的结症;

如何使用迭代器模式解决这个问题呢?其实Java内部提供了迭代器接口,咱们也能够本身建立一个迭代器接口,具体以下。

3.2 使用迭代器模式解决弊端

我准备直接使用Java提供的迭代器接口实现,先来看看类图和代码:

代码以下:

// 系统菜单,实现了迭代器接口
public class SystemMenu implements Iterator<String> {

    // 私有化数据结构
    private List<String> menus = new ArrayList<>();
    // 私有化的索引
    private int index;

    public SystemMenu() {
        menus.add("系统菜单1");
        menus.add("系统菜单2");
        menus.add("系统菜单3");
        index = 0;
    }

    @Override
    public boolean hasNext() {
        // 判断集合是否还有元素
        return index < menus.size();
    }

    @Override
    public String next() {
        // 返回集合的元素
        String name = menus.get(index);
        // 索引+1
        index++;
        return name;
    }
}
// 业务菜单,实现了迭代器接口
public class BusinessMenu implements Iterator<String> {

    // 私有化数据结构
    private String[] menus = new String[3];
    // 私有化的索引
    private int index;

    public BusinessMenu() {
        menus[0] = "业务菜单A";
        menus[1] = "业务菜单B";
        menus[2] = "业务菜单C";
        index = 0;
    }

    @Override
    public boolean hasNext() {
        // 判断数组是否还有元素
        return index < menus.length;
    }

    @Override
    public String next() {
        // 返回数组中的元素
        String name = menus[index];
        // 索引+1
        index++;
        return name;
    }
}
public class Client {

    public static void main(String[] args) {
        printMenu(new SystemMenu());
        System.out.println();
        printMenu(new BusinessMenu());
    }

    public static void printMenu(Iterator<String> iterator) {
        // 使用迭代器,循环输出
        while(iterator.hasNext()) {
            System.out.println(iterator.next());
        }
    }

}
输出:
系统菜单1
系统菜单2
系统菜单3

业务菜单A
业务菜单B
业务菜单C

上面就是迭代器模式,很简单,咱们在平常开发中常常会使用到,许多的工具类都会提供迭代器让咱们进行遍历。

可是仍是说说如何解决了弊端:

  • 数据安全:将数据结构封装到了对象内部,外部没法对其进行操做;
  • 封装遍历:实现了Java提供的迭代器接口(泛型的),将迭代的步骤封装成hasNext和next两个函数;

4、总结

迭代器模式提高了类的内聚性!

类的每一个责任都有改变的潜在区域。超过一个责任,意味着超过一个改变区域。

这个原则告诉咱们,尽可能让每个类保持单一责任。

内聚(Cohesion)这个术语,它用来度量一个类或者模块是否紧密地达到单一目的或责任。

当一个模块或一个类被设计成只支持一组相关的功能时,咱们说它具备高内聚;反之,当被设计成支持一组不相关的功能时,咱们说它具备低内聚。

内聚是一个比单一职责更广泛的概念,但二者其实关系是很密切的。遵照这个原则的类更容易有很高的凝聚力,并且比背负许多职责的低内聚类更容易维护。

以上就是我对迭代器模式的一些理解,有不足之处请你们指出,谢谢。

相关文章
相关标签/搜索