迭代器模式
实现结构以及优点
迭代器模式也叫游标模式, 将集合对象的遍历操作从集合类中拆分出来,放到迭代器类中,让两者的职责更加单一。

容器对象通过依赖注入传递到迭代器类中。遍历集合一般有三种方式:for 循环、foreach 循环、迭代器遍历。后两种本质上属于一种,都可以看作迭代器遍历。相对于 for 循环遍历,利用迭代器来遍历有下面三个优势:
- 迭代器模式封装集合内部的复杂数据结构,开发者不需要了解如何遍历,直接使用容器提供的迭代器即可;
- 迭代器模式将集合对象的遍历操作从集合类中拆分出来,放到迭代器类中,让两者的职责更加单一;
- 迭代器模式让添加新的遍历算法更加容易,更符合开闭原则。除此之外,因为迭代器都实现自相同的接口,在开发中基于接口而非实现编程,替换迭代器也变得更加容易。
遍历集合的同时增删元素的问题
可能导致不可预期行为, 也叫未决行为.
[a,b,c]此时cursor=1, 指向b
添加元素示例:
如果在b前添加个d, [a,d,b,c], 继续遍历, b会被重复遍历; 但在b后添加元素无影响
删除元素示例:
如果删除a,[b,c], 继续遍历, c无法遍历到; 但在b后删除元素无影响
为了避免这一情况, 有两种解决方案
- 遍历时不允许增删元素—>由于无法判断遍历的终止, 因此不适合
- 遍历时增删元素后报错—>jdk采用
- 在 ArrayList 中定义一个成员变量 modCount,记录集合被修改的次数,集合每调用一次增加或删除元素的函数,就会给 modCount 加 1。
- 当通过调用集合上的 iterator() 函数来创建迭代器的时候,我们把 modCount 值传递给迭代器的 expectedModCount 成员变量,之后每次调用迭代器上的 hasNext()、next()、currentItem() 函数,我们都会检查集合上的 modCount 是否等于 expectedModCount,也就是看,在创建完迭代器之后,modCount 是否改变过。
- 如果两个值不相同,那就说明集合存储的元素已经改变了,要么增加了元素,要么删除了元素,所以抛出运行时异常 ```java
public class ArrayIterator implements Iterator { private int cursor; private ArrayList arrayList; private int expectedModCount;
public ArrayIterator(ArrayList arrayList) { this.cursor = 0; this.arrayList = arrayList; this.expectedModCount = arrayList.modCount; }
@Override public boolean hasNext() { checkForComodification(); return cursor < arrayList.size(); }
@Override public void next() { checkForComodification(); cursor++; }
@Override public Object currentItem() { checkForComodification(); return arrayList.get(cursor); }
private void checkForComodification() { if (arrayList.modCount != expectedModCount) throw new ConcurrentModificationException(); } }
//代码示例
public class Demo {
public static void main(String[] args) {
List
Iterator<String> iterator = names.iterator();iterator.next();names.remove("a");iterator.next();//抛出ConcurrentModificationException异常
} }
<a name="3grjz"></a>### iterator的remove()方法实现1. next()方法返回cursor()位置的元素, 将cursor保存在lastRet, 并将cursor+11. remove()方法删除lastRet位置的元素, 并将lastRet赋值给cursor示例<br />假设a元素对应位置为1<br />E a=iterator.next();//游标变为1+1位置, lastRet在1位置<br />iterator.remove();//删除a, 将游标重置到1位置```javapublic class ArrayList<E> {transient Object[] elementData;private int size;public Iterator<E> iterator() {return new Itr();}private class Itr implements Iterator<E> {int cursor; // index of next element to returnint lastRet = -1; // index of last element returned; -1 if no suchint expectedModCount = modCount;Itr() {}public boolean hasNext() {return cursor != size;}@SuppressWarnings("unchecked")public E next() {checkForComodification();int i = cursor;if (i >= size)throw new NoSuchElementException();Object[] elementData = ArrayList.this.elementData;if (i >= elementData.length)throw new ConcurrentModificationException();cursor = i + 1;return (E) elementData[lastRet = i];}public void remove() {if (lastRet < 0)throw new IllegalStateException();checkForComodification();try {ArrayList.this.remove(lastRet);cursor = lastRet;lastRet = -1;expectedModCount = modCount;} catch (IndexOutOfBoundsException ex) {throw new ConcurrentModificationException();}}}}
访问者模式
业务代码示例:
public abstract class ResourceFile {protected String filePath;public ResourceFile(String filePath) {this.filePath = filePath;}}public class PdfFile extends ResourceFile {public PdfFile(String filePath) {super(filePath);}//...}//...PPTFile、WordFile代码省略...public class Extractor {public void extract2txt(PPTFile pptFile) {//...System.out.println("Extract PPT.");}public void extract2txt(PdfFile pdfFile) {//...System.out.println("Extract PDF.");}public void extract2txt(WordFile wordFile) {//...System.out.println("Extract WORD.");}}public class ToolApplication {public static void main(String[] args) {Extractor extractor = new Extractor();List<ResourceFile> resourceFiles = listAllResourceFiles(args[0]);for (ResourceFile resourceFile : resourceFiles) {extractor.extract2txt(resourceFile);}}private static List<ResourceFile> listAllResourceFiles(String resourceDirectory) {List<ResourceFile> resourceFiles = new ArrayList<>();//...根据后缀(pdf/ppt/word)由工厂方法创建不同的类对象(PdfFile/PPTFile/WordFile)resourceFiles.add(new PdfFile("a.pdf"));resourceFiles.add(new WordFile("b.word"));resourceFiles.add(new PPTFile("c.ppt"));return resourceFiles;}}
上面的代码是编译通过不了的,第 37 行会报错
多态是一种动态绑定,可以在运行时获取对象的实际类型,来运行实际类型对应的方法。而函数重载是一种静态绑定,在编译时并不能获取对象的实际类型,而是根据声明类型执行声明类型对应的方法。
访问者模式
public abstract class ResourceFile {protected String filePath;public ResourceFile(String filePath) {this.filePath = filePath;}abstract public void accept(Visitor vistor);}public class PdfFile extends ResourceFile {public PdfFile(String filePath) {super(filePath);}@Overridepublic void accept(Visitor visitor) {visitor.visit(this);}//...}//...PPTFile、WordFile跟PdfFile类似,这里就省略了...public interface Visitor {void visit(PdfFile pdfFile);void visit(PPTFile pdfFile);void visit(WordFile pdfFile);}public class Extractor implements Visitor {@Overridepublic void visit(PPTFile pptFile) {//...System.out.println("Extract PPT.");}@Overridepublic void visit(PdfFile pdfFile) {//...System.out.println("Extract PDF.");}@Overridepublic void visit(WordFile wordFile) {//...System.out.println("Extract WORD.");}}public class Compressor implements Visitor {@Overridepublic void visit(PPTFile pptFile) {//...System.out.println("Compress PPT.");}@Overridepublic void visit(PdfFile pdfFile) {//...System.out.println("Compress PDF.");}@Overridepublic void visit(WordFile wordFile) {//...System.out.println("Compress WORD.");}}public class ToolApplication {public static void main(String[] args) {Extractor extractor = new Extractor();List<ResourceFile> resourceFiles = listAllResourceFiles(args[0]);for (ResourceFile resourceFile : resourceFiles) {resourceFile.accept(extractor);}Compressor compressor = new Compressor();for(ResourceFile resourceFile : resourceFiles) {resourceFile.accept(compressor);}}private static List<ResourceFile> listAllResourceFiles(String resourceDirectory) {List<ResourceFile> resourceFiles = new ArrayList<>();//...根据后缀(pdf/ppt/word)由工厂方法创建不同的类对象(PdfFile/PPTFile/WordFile)resourceFiles.add(new PdfFile("a.pdf"));resourceFiles.add(new WordFile("b.word"));resourceFiles.add(new PPTFile("c.ppt"));return resourceFiles;}}

访问者模式
允许一个或者多个操作应用到一组对象上,解耦操作和对象本身。
一般来说,访问者模式针对的是一组类型不同的对象(PdfFile、PPTFile、WordFile)。不过,尽管这组对象的类型是不同的,但是,它们继承相同的父类(ResourceFile)或者实现相同的接口。在不同的应用场景下,我们需要对这组对象进行一系列不相关的业务操作(抽取文本、压缩等),但为了避免不断添加功能导致类(PdfFile、PPTFile、WordFile)不断膨胀,职责越来越不单一,以及避免频繁地添加功能导致的频繁代码修改,我们使用访问者模式,将对象与操作解耦,将这些业务操作抽离出来,定义在独立细分的访问者类(Extractor、Compressor)中。
备忘录模式
备忘录模式也叫快照模式,具体来说,就是在不违背封装原则的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,以便之后恢复对象为先前的状态。这个模式的定义表达了两部分内容:一部分是,存储副本以便后期恢复;另一部分是,要在不违背封装原则的前提下,进行对象的备份和恢复。
对于大对象的备份来说,备份占用的存储空间会比较大,备份和恢复的耗时会比较长。针对这个问题,不同的业务场景有不同的处理方式。比如,只备份必要的恢复信息,结合最新的数据来恢复;再比如,全量备份和增量备份相结合,低频全量备份,高频增量备份,两者结合来做恢复。
示例代码:
public class InputText {private StringBuilder text = new StringBuilder();public String getText() {return text.toString();}public void append(String input) {text.append(input);}public Snapshot createSnapshot() {return new Snapshot(text.toString());}public void restoreSnapshot(Snapshot snapshot) {this.text.replace(0, this.text.length(), snapshot.getText());}}public class Snapshot {private String text;public Snapshot(String text) {this.text = text;}public String getText() {return this.text;}}public class SnapshotHolder {private Stack<Snapshot> snapshots = new Stack<>();public Snapshot popSnapshot() {return snapshots.pop();}public void pushSnapshot(Snapshot snapshot) {snapshots.push(snapshot);}}public class ApplicationMain {public static void main(String[] args) {InputText inputText = new InputText();SnapshotHolder snapshotsHolder = new SnapshotHolder();Scanner scanner = new Scanner(System.in);while (scanner.hasNext()) {String input = scanner.next();if (input.equals(":list")) {System.out.println(inputText.toString());} else if (input.equals(":undo")) {Snapshot snapshot = snapshotsHolder.popSnapshot();inputText.restoreSnapshot(snapshot);} else {snapshotsHolder.pushSnapshot(inputText.createSnapshot());inputText.append(input);}}}}
命令模式
命令模式将请求(命令)封装为一个对象,这样可以使用不同的请求参数化其他对象(将不同请求依赖注入到其他对象),并且能够支持请求(命令)的排队执行、记录日志、撤销等(附加控制)功能。
解释器模式
解释器模式为某个语言定义它的语法(或者叫文法)表示,并定义一个解释器用来处理这个语法。
解释器模式只在一些特定的领域会被用到,比如编译器、规则引擎、正则表达式。
它的代码实现的核心思想,就是将语法解析的工作拆分到各个小类中,以此来避免大而全的解析类。一般的做法是,将语法规则拆分成一些小的独立的单元,然后对每个单元进行解析,最终合并为对整个语法规则的解析
中介模式
中介模式定义了一个单独的(中介)对象,来封装一组对象之间的交互。将这组对象之间的交互委派给与中介对象交互,来避免对象之间的直接交互。
观察者模式和中介模式都是为了实现参与者之间的解耦,简化交互关系。两者的不同在于应用场景上。在观察者模式的应用场景中,参与者之间的交互比较有条理,一般都是单向的,一个参与者只有一个身份,要么是观察者,要么是被观察者。而在中介模式的应用场景中,参与者之间的交互关系错综复杂,既可以是消息的发送者、也可以同时是消息的接收者。
