备忘录模式(Memento)
一、備忘錄模式介紹
備忘錄模式:在不破壞封裝性的前提下,捕獲一個對象的內部狀態,并在該對象之外保存這個狀態。這樣以后就可將該對象恢復到原先保存的狀態。
例如:
1.office重新打開時的恢復功能。
2.事務的回滾操作
備忘錄模式UML圖:
Originator(發起人):負責創建一個備忘錄Memento,用以記錄當前時刻它的內部狀態,并可使用備忘錄恢復內部狀態。
Originator可根據需要決定Memento存儲Originator的哪些內部狀態
Memento(備忘錄):負責存儲Originator對象的內部狀態,并可防止Originator以外的其他對象訪問備忘錄Memento。
備忘錄有兩個接口,Caretaker只能看到備忘錄的窄接口,它只能將備忘錄傳遞給其他對象。Originator能夠看到一個寬接口,允許它訪問返回到
先前狀態所需的所有數據。
Caretaker(管理者):負責保存好備忘錄Memento,不能對備忘錄的內存進行操作或檢查。
?
二、備忘錄模式代碼實現
以一個Emp實體對象來作為例子
首先,創建一個發起人:發起人內部保存著需要備忘的屬性,它負責創建一個備忘錄Memento,用以記錄當前時刻它的內部狀態,并可使用備忘錄恢復內部狀態。
?
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //發起人:發起人內部有自身的內部狀態,并且發起人可以創建備忘錄和恢復備忘錄 public class EmpOriginator { ????//需要備份的自身屬性 ????private String ename; ????private int age; ????private double salary;????? ????//備份 ????public EmpMemento memento(){ ????????return new EmpMemento(this);//將當前自身對象備份 ????}????? ????//恢復 ????public void recovery(EmpMemento emp){ ????????this.ename = emp.getEname(); ????????this.age = emp.getAge(); ????????this.salary = emp.getSalary(); ????}?????? ????//省略get,set和帶參構造器?? } |
創建備忘錄對象:備忘錄就是用來備份發起人的數據,所以構造器需要提供一個發起人對象
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 | //備忘錄對象 public class EmpMemento { ????//自身屬性 ????private String ename; ????private int age; ????private double salary;? ????//構造備忘錄對象時,需要傳入一個需要備忘的對象(發起人) ????public EmpMemento(EmpOriginator emp) { ????????this.ename = emp.getEname(); ????????this.age = emp.getAge(); ????????this.salary = emp.getSalary(); ????} ????//省略3個屬性的set,get方法 } |
開始創建一個備忘錄的管理者
| 1 2 3 4 5 6 7 8 9 10 11 | //管理者:管理備忘錄對象 public class CareTaker { ????//需要管理的備忘錄對象,這里也可以使用一個list容器來存儲。這樣可以備份多個點?? ????private EmpMemento empMemento; ????public EmpMemento getEmpMemento() { ????????return empMemento; ????} ????public void setEmpMemento(EmpMemento empMemento) { ????????this.empMemento = empMemento; ????}?? } |
單次備份測試:測試只能備份一次的備忘錄
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | public static void main(String[] args) { ????CareTaker taker = new CareTaker();//構建一個備忘錄管理者 ????//構建發起人 ????EmpOriginator emp = new EmpOriginator("張三", 20, 4000); ????System.out.println("第一次:"+emp.getEname()+"---"+emp.getAge()+"---"+emp.getSalary()); ????//備份 ????taker.setEmpMemento(emp.memento()); ????? ????//備份完了后再修改 ????emp.setEname("李四"); ????emp.setAge(30); ????emp.setSalary(50000); ????//然后再次打印 ????System.out.println("修改后:"+emp.getEname()+"---"+emp.getAge()+"---"+emp.getSalary()); ????? ????//開始恢復 ????emp.recovery(taker.getEmpMemento()); ????System.out.println("恢復后:"+emp.getEname()+"---"+emp.getAge()+"---"+emp.getSalary()); } |
測試結果如下:
????????????????第一次:張三---20---4000.0
????????????????修改后:李四---30---50000.0
????????????????恢復后:張三---20---4000.0
但是,這樣的效果是只能備份一次。有時候我們需要備份多個點,根據需要來還原具體哪次的數據
多次備份
使用Stack來存儲備份數據,進行多次備份。用Stack的好處是Stack是后進先出的,也就是說:你最近一次備份的數據會優先獲取到。
修改管理者中的代碼:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | import java.util.Stack; //管理者:管理備忘錄對象 public class CareTaker { ????//需要管理的備忘錄對象,這里也可以使用一個list容器來存儲。這樣可以備份多個點 ????//或者使用一個Stack棧來保存,因為Stack是后進先出的 ????private Stack<EmpMemento> stack = new Stack<EmpMemento>(); ????//備份emp數據到棧中 ????public void mementoEmp(EmpMemento emp){ ????????stack.push(emp); ????} ????//從棧中獲取最近一次備份的emp數據 ????public EmpMemento getEmpForStack(){???? ????????if (!stack.empty()) { ????????????return stack.peek();//peek只獲取,不刪除 ????????}else{ ????????????return null; ????????} ????} ????//從棧中獲取最近一次備份的emp數據,并且從棧中刪除該數據 ????public EmpMemento getEmpForStackAndRemove(){ ????????if (!stack.empty()) { ????????????return stack.pop();//pop獲取后刪除該元素 ????????}else{ ????????????return null; ????????}?????? ????}?? } |
再次測試:后進先出,也就是說。恢復的話恢復的是最后一次備份的數據
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | public static void main(String[] args) { ????CareTaker taker = new CareTaker();//構建一個備忘錄管理者 ????//構建發起人 ????EmpOriginator emp = new EmpOriginator("張三", 20, 4000); ????System.out.println("第一次:"+emp.getEname()+"---"+emp.getAge()+"---"+emp.getSalary()); ????//第1次備份 ????taker.mementoEmp(emp.memento()); ????? ????//備份完了后再修改 ????emp.setEname("李四"); ????emp.setAge(30); ????emp.setSalary(50000); ????//然后再次打印 ????System.out.println("1修改后:"+emp.getEname()+"---"+emp.getAge()+"---"+emp.getSalary()); ????//第2次備份 ????taker.mementoEmp(emp.memento()); ????? ????//備份完了后再修改 ????emp.setEname("李四2"); ????emp.setAge(32); ????emp.setSalary(52000); ????//然后再次打印 ????System.out.println("2修改后:"+emp.getEname()+"---"+emp.getAge()+"---"+emp.getSalary()); ????//第3次備份 ????taker.mementoEmp(emp.memento()); ????//備份完了后再修改 ????emp.setEname("李四3"); ????emp.setAge(33); ????emp.setSalary(32000); ????//然后再次打印 ????System.out.println("3修改后:"+emp.getEname()+"---"+emp.getAge()+"---"+emp.getSalary()); ????//第4次備份 ????taker.mementoEmp(emp.memento()); ????? ????//開始恢復 ????emp.recovery(taker.getEmpForStack());//恢復的是第4次備份的數據(3修改后) ????System.out.println("恢復后:"+emp.getEname()+"---"+emp.getAge()+"---"+emp.getSalary()); } |
測試結果如下:
????????????第一次:張三---20---4000.0
????????????1修改后:李四---30---50000.0
????????????2修改后:李四2---32---52000.0
????????????3修改后:李四3---33---32000.0
????????????恢復后:李四3---33---32000.0
當然,也可以恢復之后把它從棧中刪除。
測試:恢復之前從棧中刪除兩個最近的數據
| 1 2 3 4 5 | //開始恢復 taker.getEmpForStackAndRemove();//刪除最近一次的備份 taker.getEmpForStackAndRemove();//刪除最近一次的備份 emp.recovery(taker.getEmpForStack());//此時獲取的是原來未刪除時倒數第二次的備份 System.out.println("恢復后:"+emp.getEname()+"---"+emp.getAge()+"---"+emp.getSalary()); |
此時的結果就是如下:可見,這里已經把2和3給刪除了
????????????????第一次:張三---20---4000.0
????????????????1修改后:李四---30---50000.0
????????????????2修改后:李四2---32---52000.0
????????????????3修改后:李四3---33---32000.0
????????????????恢復后:李四---30---50000.0
?
三、總結
開發中常見場景:
棋類游戲中的悔棋操作
軟件中的撤銷操作
數據庫中的事務回滾操作
常用軟件中的歷史記錄功能
?
?
Java23種設計模式學習筆記【目錄總貼】
參考資料:
大話設計模式(帶目錄完整版).pdf
HEAD_FIRST設計模式(中文版).pdf
尚學堂_高淇_java300集最全視頻教程_【GOF23設計模式】
轉載于:https://www.cnblogs.com/meet/p/5116407.html
《新程序員》:云原生和全面數字化實踐50位技術專家共同創作,文字、視頻、音頻交互閱讀總結
以上是生活随笔為你收集整理的备忘录模式(Memento)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: FreeBSD Top States
- 下一篇: OC考核测试题