设计模式学习核心与要领:
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迭代器接口来编程遍历,提高系统整体的可维护性,可扩展性。