濱海住房和城鄉(xiāng)建設(shè)局網(wǎng)站谷歌查詢關(guān)鍵詞的工具叫什么
備忘錄模式和迭代器模式
- 備忘錄模式
- 代碼示例
- 迭代器模式
- 代碼示例
- 使用迭代器遍歷集合的同時不能刪除/增加元素
- 總結(jié)
備忘錄模式
備忘錄模式,也叫快照(Snapshot)模式。
在 GoF的《設(shè)計模式》?書中,備忘錄模式是這么定義的:
Captures and externalizes an object’s internal state so that it can be restored later, all without violating encapsulation.
在不違背封裝原則的前提下,捕獲?個對象的內(nèi)部狀態(tài),并在該對象之外保存這個狀態(tài),以便之后恢復(fù)對象為先前的狀態(tài),屬于行為型模式。
備忘錄模式主要分為三個角色 :
發(fā)起人角色(Originator): 它是一個需要保存狀態(tài)的類,可以創(chuàng)建一個備忘錄/備份,并存儲它的當(dāng)前內(nèi)部狀態(tài),也可以使用備忘錄來恢復(fù)其內(nèi)部狀態(tài)。
**備忘錄角色 Memento:**存儲原發(fā)器的內(nèi)部狀態(tài)。除了發(fā)起人,備忘錄類不能被其他類創(chuàng)建和修改。一般通過將Memento類與Originator類定義在同一個包中來實現(xiàn)封裝(也可以作為內(nèi)部類),使用默認訪問標(biāo)識符來定義Memento類,即保證其包內(nèi)可見。
**負責(zé)人角色 Caretaker:**負責(zé)人又稱為管理者,它負責(zé)保存?zhèn)渫洝T谪撠?zé)人類中可以存儲一個或多個備忘錄對象,它只負責(zé)存儲備忘錄對象,而不能修改備忘錄對象(負責(zé)人類只提供備忘錄對象的讀寫接口,不提供備忘錄屬性的讀寫接口)。
代碼示例
以一個文本編輯器為例,用戶可以不斷輸入文本內(nèi)容,也可以撤銷上次輸入的內(nèi)容,按照備忘錄模式實現(xiàn)如下:
public class InputText {private StringBuilder text = new StringBuilder();public void input(String input) {text.append(input);}public String show() {return text.toString();}public Snapshot createSnapshot() {return new Snapshot(this.text.toString());}public void restoreSnapshot(Snapshot snapshot) {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 text;}
}
public class SnapshotHolder {private Stack<Snapshot> stack = new Stack<>();public Snapshot getSnapshot() {return stack.pop();}public void pushSnapshot(Snapshot snapshot) {stack.push(snapshot);}
}
public class Test {public static void main(String[] args) {SnapshotHolder snapshotHolder = new SnapshotHolder();InputText text = new InputText();text.input("你好");System.out.println(text.show());snapshotHolder.pushSnapshot(text.createSnapshot());text.input("我是Xiaoming");System.out.println(text.show());System.out.println("=============回撤==========");text.restoreSnapshot(snapshotHolder.getSnapshot());System.out.println(text.show());}}
對于?對象的備份來說,備份占?的存儲空間會?較?,備份和恢復(fù)的耗時會?較?。針對這個問題,不同的業(yè)務(wù)場景有不同的處理?式。?如,只備份必要的恢復(fù)信息,結(jié)合最新的數(shù)據(jù)來恢復(fù);
再?如,全量備份和增量備份相結(jié)合,低頻全量備份,?頻增量備份,兩者結(jié)合來做恢復(fù)。
迭代器模式
迭代器模式(Iterator Design Pattern),也叫作游標(biāo)模式(Cursor Design Pattern),它提供一種順序訪問容器或者集合對象元素的方法,而又無需暴露集合內(nèi)部表示。屬于行為型模式
迭代器模式,一般包含四個角色
抽象容器 : 提供創(chuàng)建迭代器的接口
具體容器 : 創(chuàng)建具體迭代器
抽象迭代器 : 定義遍歷元素的接口
具體迭代器 : 實現(xiàn)元素遍歷行為
一般來說我們會在容器中定義
iterator()
?法,?來創(chuàng)建迭代器。迭代器接?中需要定義 hasNext()、next() 兩個最基本的?法用于遍歷。
代碼示例
public interface Iterator<T> {public boolean hasNext();T next();
}
public class ListIterator implements Iterator<String>{private List<String> queue;private int cursor = 0;public ListIterator(List<String> queue) {this.queue = queue;}@Overridepublic boolean hasNext() {return cursor != queue.size();}@Overridepublic String next() {String name = queue.get(cursor);cursor++;return name;}
}
public interface Queue {void add(String name);void remove(String name);Iterator iterator();}
public class QueueImpl implements Queue{private List<String> list = new ArrayList<>();@Overridepublic void add(String name) {list.add(name);}@Overridepublic void remove(String name) {list.remove(name);}@Overridepublic Iterator iterator() {return new ListIterator(list);}
}
public class Test {public static void main(String[] args) {QueueImpl queue = new QueueImpl();queue.add("小明");queue.add("小花");queue.add("王一");Iterator<String> iterator = queue.iterator();while (iterator.hasNext()) {String name = iterator.next();System.out.println(name);}}
}
使用迭代器遍歷集合的同時不能刪除/增加元素
在通過迭代器來遍歷集合元素的同時,增加或者刪除集合中的元素,有可能會導(dǎo)致某個元素被重復(fù)遍歷或遍歷不到。(這是因為為了保持數(shù)組存儲數(shù)據(jù)的連續(xù)性,數(shù)組的刪除/插入操作會涉及元素的搬移)
針對這種問題有兩種解決?案:?種是遍歷的時候不允許增刪元素,另?種是增刪元素之后讓遍歷報錯。
第一種實現(xiàn)比較困難,我們可以方便的知道遍歷開始的時間點,但是我們無法知曉遍歷什么時候結(jié)束(它可能獲取到某個滿足條件的值之后就結(jié)束遍歷了),而通過新方法告知集合遍歷結(jié)束也比較容易遺漏。
Java采用的是第二種方法: 增刪元素之后讓遍歷報錯
Java在ArrayList 中定義了一個成員變量modCount
,記錄集合被修改的次數(shù)。集合每次新增或者刪除元素,就會給modCount+1。
當(dāng)用戶創(chuàng)建迭代器的時候,我們把modCount 值傳遞給迭代器的 expectedModCount 成員變量,之后每次調(diào)?迭代器上的next()方法,我們都會檢查集合上的 modCount 是否等于expectedModCount,也就是判斷在創(chuàng)建完迭代器之后,集合是否改變過。
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];}final void checkForComodification() {if (modCount != expectedModCount)throw new ConcurrentModificationException();}}
總結(jié)
集合遍歷?般有三種?式:for 循環(huán)、foreach 循環(huán)、迭代器遍歷。
后兩種本質(zhì)上屬于?種,都可以看作迭代器遍歷。
相對于 for 循環(huán)遍歷,利?迭代器來遍歷有下?三個優(yōu)勢:
- 迭代器模式封裝集合內(nèi)部的復(fù)雜數(shù)據(jù)結(jié)構(gòu),開發(fā)者不需要了解如何遍歷,直接使?容器提供的迭代器即可
- 迭代器模式將集合對象的遍歷操作從集合類中拆分出來,放到迭代器類中,讓兩者的職責(zé)更加單? (對于復(fù)雜的數(shù)據(jù)結(jié)構(gòu),圖,樹等,客戶端自己實現(xiàn)遍歷算法比較復(fù)雜也容易出錯)
- 因為迭代器都實現(xiàn)?相同的接?,在開發(fā)中,基于接???實現(xiàn)編程,替換/新增迭代器也變得更加容易