设计模式-迭代器

设计模式学习核心与要领:

1.每个模式长什么样子?

2.如果不用这个模式 会有什么问题,如果用了这个模式 会有什么好处?

3.一般在企业开发中,这个模式是怎么用的?即在实际的企业开发场景里面,是如何应用的?

最重要的是(要在项目里去实践),在一个项目中,比如电商系统里,将所有这些设计模式,全部在真实复杂的电商业务中去深入的使用,结合业务来设计这个模式如何运用到业务中,基于设计模式 开发出 高内聚、低耦合、高度可扩展、维护成本低、逻辑清晰、易读易懂的代码。

面试技巧:考察 设计模式的运用和实践功底。

首先,结合自己做过的复杂业务系统,给人家先讲一讲业务;

然后,说一下 如果不用这个模式来设计这个业务的代码实现,可能会导致代码 会有什么问题;

然后,但是你当时合理的运用了设计模式(不能生搬硬套,导致为了用模式而用模式),将代码设计的结构如何优良,可扩展性如何的好,维护性如何的好。

迭代器模式:本质就是面向接口编程

需求:迭代教室里的学生,设计一个Classroom类 和 Student类。

为了方便调用和演示,把他们都写成一个类里的静态内部类的形式(如果写在类的外面,就不能添加static);

Student类只有一个String属性name,Classroom只有一个Student数组属性students,并分别生成有参构造器,get/set方法 和 toString方法;

然后,写一个main方法,new出两个学生,student1小灵儿和student2小帆儿,并添加到Student[2]数组students中,

然后,再new出 一个Classroom,将students数组set到Classroom中

最后,再从Classroom中获取到学生数组,进行遍历。

那么这时,出于一定的原因,我不想用数组来储存Classroom中的Student学生了,我用Map来储存Map<String, Student>,那么关于使用Classroom有关的代码(将学生set到Classroom和对Classroom进行迭代),就都要随之改动。即牵一发而动全身。

小结:如果不用任何设计模式,直接去遍历一个类中的集合,一旦这个类中对集合的使用改变了,比如从数组到map或List等,那么你迭代的这块代码,就要改动。

如果说代码和业务逻辑很复杂,同时集合类的实现和遍历代码的实现,是两个人开发的,这个成本就很高了,因为代码报错了,大家又要协调,然后又要改动。

简单来说,这种代码的可扩展性,可维护性都很差,因为不好扩展,一扩展就要改代码,不好维护。

public class NonPatternApplication {

    public static void main(String[] args) {
        Student student1 = new Student("小灵儿");
        Student student2 = new Student("小帆儿");

//        Student[] students = new Student[2];
//        students[0] = student1;
//        students[1] = student2;
        Map<String, Student> students = new HashMap<String, Student>();
        students.put(student1.name, student1);
        students.put(student2.name, student2);

        Classroom classroom = new Classroom();
        classroom.setStudents(students);

//        Student[] resultStudents = classroom.getStudents();
//        for (Student resultStudent : resultStudents) {
//            System.out.println(resultStudent);
//        }
        Map<String, Student> resultStudents = classroom.getStudents();
        for (Student resultStudent : resultStudents.values()) {
            System.out.println(resultStudent);
        }
    }

    public static class Student{
        public Student(String name) {
            this.name = name;
        }

        private String name;

        public String getName() {
            return name;
        }

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

        @Override
        public String toString() {
            return "Student{" +
                    "name='" + name + '\'' +
                    '}';
        }
    }

    /*public static class Classroom {

        private Student[] students;

        public Student[] getStudents() {
            return students;
        }

        public void setStudents(Student[] students) {
            this.students = students;
        }
    }*/

    public static class Classroom {

        private Map<String, Student> students;

        public Map<String, Student> getStudents() {
            return students;
        }

        public void setStudents(Map<String, Student> students) {
            this.students = students;
        }
    }
}

 下面通过迭代器模式实现开发如下:

public class PatternApplication {

    public static void main(String[] args) {
        Student student1 = new Student("小灵儿");
        Student student2 = new Student("小帆儿");

        Classroom classroom = new Classroom(2);
        classroom.addStudent(student1);
        classroom.addStudent(student2);

        // 这里我们遍历教室里的学生,先是拿到教室的这个迭代器,
        // 然后面向迭代器这个接口提供的hasNext()和next()方法,去做的遍历开发
        Iterator iterator = classroom.iterator();
        while (iterator.hasNext()) {
            Student student = (Student) iterator.next();
            System.out.println(student);
        }

    }
    /**定义一个我们自己的迭代器接口*/
    public interface Iterator {
        // 集合是否有下一个元素
        public abstract boolean hasNext();
        // 获取这个集合的下一个元素
        public abstract Object next();
    }
    /**代表了一个集合类*/
    public interface Aggregate {
        // 此方法可以返回这个集合对应的一个迭代器
        public abstract Iterator iterator();
    }

    /**学生类*/
    public static class Student{
        public Student(String name) {
            this.name = name;
        }

        private String name;

        public String getName() {
            return name;
        }

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

        @Override
        public String toString() {
            return "Student{" +
                    "name='" + name + '\'' +
                    '}';
        }
    }

    /**
     * 教室迭代器(含有Classroom的引用)
     * 负责两件事:1.数据集合它有没有下一个元素;2.获取数据集合当前的这个元素
     */
    public static class ClassroomIterator implements Iterator{
        // 因为含有Classroom的引用,就可以从Classroom里面,拿到它对应的数据集合
        private Classroom classroom;
        private int index;

        public ClassroomIterator(Classroom classroom) {
            this.classroom = classroom;
            // 刚开始初始的index是0
            this.index = 0;
        }

        // 判断 Classroom 里面是否含有下一个学生,
        // 如果index小于Classroom中数据集合的长度,才能够获取下一个元素,就是说数组还没有遍历完成
        // 假设此时index是2,classroom的length是2,classroom可以遍历的数组的offset只能是0和1
        @Override
        public boolean hasNext() {
            if (index < classroom.getLength()) {
                return true;
            } else {
                return false;
            }
        }

        // 从数组中获取当前的学生,同时将index往下移动一位
        @Override
        public Object next() {
            Student student = classroom.getStudent(index);
            index++;
            return student;
        }
    }
    /**教室类*/
    public static class Classroom implements Aggregate{

        private Student[] students;
        // last相当于数据的长度(数组指针)
        private int last = 0;

        public Classroom(int size) {
            this.students = new Student[size];
        }

        public Student getStudent(int index) {
            return students[index];
        }

        // 每往 数据集合里添加一个元素,这个last就会加1
        public void addStudent(Student student) {
            this.students[last] = student;
            last++;
        }

        public int getLength() {
            return last;
        }

        // 返回一个教室迭代器,其中封装了教室自己,让迭代器可以获取教室中的数据
        public Iterator iterator() {
            return new ClassroomIterator(this);
        }
    }
}

这个时候,我们遍历集合的开发,实际上是面向迭代器这个接口,提供的hasNext()和next()方法,去做的开发。

如果我们的教室类Classroom中的实现,发生了改变,比如用Map代替数组,嗯~HashMap是无序的,不能通过下标获取,因为它这个遍历是要考虑一个顺序,我不用Map来演示。

这里用List来演示,List<Student> students;,教室类Classroom实现如下:

 1     /**教室类*/
 2     public static class Classroom implements Aggregate{
 3         //private Student[] students;
 4         private List<Student> students;
 5         // last相当于数据的长度(数组指针)
 6         private int last = 0;
 7 
 8         public Classroom(int size) {
 9             //this.students = new Student[size];
10             this.students = new ArrayList<Student>(size);
11         }
12 
13         public Student getStudent(int index) {
14             //return students[index];
15             return students.get(index);
16         }
17 
18         // 每往 数据集合里添加一个元素,这个last就会加1
19         public void addStudent(Student student) {
20             //this.students[last] = student;
21             this.students.add(student);
22             last++;
23         }
24 
25         public int getLength() {
26             return last;
27         }
28 
29         // 返回一个教室迭代器,其中封装了教室自己,让迭代器可以获取教室中的的数据
30         public Iterator iterator() {
31             return new ClassroomIterator(this);
32         }
33     }

注意:插入代码时,不要勾选行号,否则重新编辑,复制时会连同行号一起复制,影响修改效率。

这时,我们会发现,所有代码中,连迭代器的逻辑都不用变,因为它都是基于Classroom的接口去实现的,只是改了Classroom自己本身而已;然后Iterator去迭代集合的这段代码就更不用变了。所以,迭代器模式,它的核心的用意就是,封装你的集合迭代逻辑。即把集合迭代的逻辑,给封装在了Iterator里面,然后,对数据进行操作的一些逻辑,给封装在了Classroom这个里面,这样,在对Classroom进行迭代的时候,只要面向Iterator去编程就可以了。Classroom里面的这个(需要迭代的)集合变化了以后,实际上来说,对于迭代它的执行者(你的这个迭代方的代码),只是面向迭代器这个接口去遍历,根本就没有任何的变化。

最后,在实际企业的应用场景里面,我们其实很少自己直接去写迭代器的模式,因为它一般都是在集合编程的时候去使用的,一般来说,我们要遍历一个集合的时候,我们会让那个集合类,返回一个这个集合对应的迭代器就可以了,而不是说直接去获取到那个集合,所以我们可以面向迭代器的这个接口去实现,因为jdk已经给我们封装好了迭代器模式了。用jdk提供给我们的iterator,改动如下:

    public static void main(String[] args) {
        Student student1 = new Student("小灵儿");
        Student student2 = new Student("小帆儿");

        Classroom classroom = new Classroom(2);
        classroom.addStudent(student1);
        classroom.addStudent(student2);

        // 这里我们遍历教室里的学生,先是拿到教室的这个迭代器,
        // 然后面向迭代器这个接口提供的hasNext()和next()方法,去做的遍历开发
        //java.util.Iterator<Student> iterator = classroom.iterator();
        java.util.Iterator iterator = classroom.iterator();
        while (iterator.hasNext()) {
            Student student = (Student) iterator.next();
            System.out.println(student);
        }
    }

    /**代表了一个集合类*/
    public interface Aggregate {
        // 此方法可以返回这个集合对应的一个迭代器
        //public abstract java.util.Iterator<Student> iterator();
        public abstract java.util.Iterator iterator();
    }

    /**教室类*/
    public static class Classroom implements Aggregate{
        ... ...

        /*
         * 返回一个教室迭代器,其中封装了教室自己,让迭代器可以获取教室中的的数据
         * 因为jdk已经封装了iterator模式,所以无需自己再去写一个迭代器,所以,这里我们既可以返回自己写的迭代器ClassroomIterator,
         * 也可以返回一个jdk已经给我们提供好了的List集合的iterator,迭代器再java.util包下
         */
        //public java.util.Iterator<Student> iterator() {
        public java.util.Iterator iterator() {
            return students.iterator();
        }
    }

注意:

1. java.util.Iterator<Student> 与 java.util.Iterator,泛型可加可不加,都不会报错,代码正常执行。

2. 插入代码时,代码折叠起来,应该也是很优雅的。

总结:

1. 面向Iterator接口编程,无论底层的数据结构 和 迭代算法如果变化,调用这都不用修改代码;高内聚,低耦合,漂亮;

2.iterator模式一般都是在集合编程中使用,尤其是如果要对集合元素遍历过程中做插入删除操作,那就用iterator,而JDK已经封装好了iterator模式,可以直接使用,是无需自己去写这个iterator模式的。除非你觉得 这个jdk提供的这个迭代器模式没有满足自己开发的需要,那么再自己去写。一般是研发底层的框架,比如提供某个数据给外部遍历,那么可以使用iterator模式自己封装迭代器。

3.如果要对某个类中的集合进行遍历,由那个集合类返回一个iterator回来,我们统一面向iterator迭代器接口来编程遍历,提高系统整体的可维护性,可扩展性。

 

end 

 

posted @ 2022-03-21 09:18  HarryVan  阅读(26)  评论(0编辑  收藏  举报