设计模式是什么鬼(备忘录)
備忘錄,備份曾經發生過的歷史記錄,以防忘記,之后便可以輕松回溯過往。想必我們曾經都干過很多蠢事導致糟糕的結果,當后悔莫及的時候已經是覆水難收了,只可惜這世界上沒有后悔藥,事后我們能做的只能去彌補過失,總結經驗。除非穿越時空,時光倒流,利用愛因斯坦狹義相對論,超越光速回到過去,破鏡重圓。
然而世界是殘酷的,人類至今最快的載人交通工具連達到光速的萬分之一都顯得遙不可及,更別說超越了。光速,宇宙間永遠無法打破的時空屏障,它像是上帝定義的常量C,將時間牢牢地套死在坐標軸上,自創世宇宙大爆炸開始就讓它不斷流逝,如同播放一部不可回退的電影一樣,暮去朝來,誰也無法打破。
但在計算機世界里,人類便是神一般的存在,各種回滾,倒退,載入歷史顯得稀松平常,例如數據庫恢復、游戲存盤載入、操作系統快照恢復、打開備份文檔、手機恢復出廠設置……為了保證極簡風格,我們這里以文檔操作來舉例說明這個設計模式。
假設某位作者要寫一部科幻小說,當他打開編輯器軟件以及創建文檔開始創作的時候,我們來思考下這個場景需要哪些類。很簡單,首先我們得有一個文檔類Doc。
public class Doc {private String title;//文章標題private String body;//文章內容public Doc(String title){//新建文檔先命名this.title = title;this.body = "";}public void setTitle(String title) {this.title = title;}public String getTitle() {return title;}public String getBody() {return body;}public void setBody(String body) {this.body = body;} }
沒什么好說的,一個簡單的Java Bean,包括標題與內容。有了文檔那一定要有編輯器去修改它了,看代碼。
public class Editor {//編輯器private Doc doc;//文檔引用public Editor(Doc doc) {System.out.println("<<<打開文檔" + doc.getTitle());this.doc = doc;show();}public void append(String txt) {System.out.println("<<<插入操作");doc.setBody(doc.getBody() + txt);show();}public void save(){System.out.println("<<<存盤操作");}public void delete(){System.out.println("<<<刪除操作");doc.setBody("");show();}private void show(){//顯示當前文本內容System.out.println(doc.getBody());System.out.println("文章結束>>>n");} }
當編輯器打開一個文檔后會持有其引用,這里我們寫在編輯器構造方法里。編輯器主要的功能當然是對文檔進行更改了,依然保持簡單的操作模擬,我們只加入append插入功能、delete清空功能,以及save存盤方法和最后的show方法用于顯示文檔內容。一切就緒,接下來看看我們的作者怎樣寫出一部驚世駭俗的科幻小說《AI的覺醒》。
public class Author {public static void main(String[] args) {Editor editor = new Editor(new Doc("《AI的覺醒》"));/*<<<打開文檔《AI的覺醒》文章結束>>>*/editor.append("第一章 混沌初開");/*<<<插入操作第一章 混沌初開文章結束>>>*/editor.append("n 正文2000字……");/*<<<插入操作第一章 混沌初開正文2000字……文章結束>>>*/editor.append("n第二章 荒漠之花n 正文3000字……");/*<<<插入操作第一章 混沌初開正文2000字……第二章 荒漠之花正文3000字……文章結束>>>*/editor.delete();/*<<<刪除操作文章結束>>> */} }
鬼才作者開始了創作,一切進行地非常順利,一氣呵成寫完了二章內容(第22行操作),于是他離開電腦去倒了杯咖啡,噩耗在此間發生了,他的熊孩子不知怎么就按下了Ctr+A,Delete觸發了第31行的操作,導致全文丟失,從內存里被清空,而且離開前作者疏忽大意也沒有進行存盤操作,這下徹底完了,5000字的心血付諸東流。
此場景該如何是好?大家都想到了Ctr+z的操作吧?它可以瞬間撤銷上一步操作并回退到前一個版本,不但讓我們有吃后悔藥的機會,而且還不需要頻繁的去存盤備份。那么這個機制是怎樣實現的呢?既然可以回溯歷史,那一定得有一個歷史備忘類來記錄每步操作后的文本狀態記錄了,它同樣是一個簡單的Java Bean。
public class History {private String body;//用于備忘文章內容public History(String body){this.body = body;}public String getBody() {return body;} }
有了這個類,我們便可以記錄文檔的內容快照了,在初始化時把文檔內容傳進來。那誰來生成這些歷史記錄呢?我們可以放在文檔類里,讓文檔類具備創建與恢復歷史記錄的功能,我們對Doc文檔類做如下修改。
public class Doc {private String title;//文章名字private String body;//文章內容public Doc(String title){//新建文檔先命名this.title = title;this.body = "";}public void setTitle(String title) {this.title = title;}public String getTitle() {return title;}public String getBody() {return body;}public void setBody(String body) {this.body = body;}public History createHistory() {return new History(body);//創建歷史記錄}public void restoreHistory(History history){this.body = history.getBody();//恢復歷史記錄} }
可以看到自第26行開始我們加入了這兩個功能,只要簡單的調用,便可以生成當下的歷史記錄,以及來去自如的恢復內容到任一歷史時刻。接下來得有對歷史記錄的邏輯控制,也就是我們期待已久的撤銷功能了,繼續對編輯器類做如下修改
public class Editor {private Doc doc;private List<History> historyRecords;// 歷史記錄列表private int historyPosition = -1;// 歷史記錄當前位置public Editor(Doc doc) {System.out.println("<<<打開文檔" + doc.getTitle());this.doc = doc; // 注入文檔historyRecords = new ArrayList<>();// 初始化歷史記錄backup();// 保存一份歷史記錄show();//顯示內容}public void append(String txt) {System.out.println("<<<插入操作");doc.setBody(doc.getBody() + txt);backup();//操作完成后保存歷史記錄show();}public void save(){System.out.println("<<<存盤操作");}public void delete(){System.out.println("<<<刪除操作");doc.setBody("");backup();//操作完成后保存歷史記錄show();}private void backup() {historyRecords.add(doc.createHistory());historyPosition++;}private void show() {// 顯示當前文本內容System.out.println(doc.getBody());System.out.println("文章結束>>>n");}public void undo() {// 撤銷操作:如按下Ctr+Z,回到過去。System.out.println(">>>撤銷操作");if (historyPosition == 0) {return;//到頭了,不能再撤銷了。}historyPosition--;//歷史記錄位置回滾一筆History history = historyRecords.get(historyPosition);doc.restoreHistory(history);//取出歷史記錄并恢復至文檔show();}// public void redo(); 省略實現代碼 }
在第3行我們加入了一個歷史記錄列表,它就像是時間軸一樣按順序地按index記錄每個時間點的歷史事件,從某種意義上看它更像是一本歷史書。接下來加入的第32行backup方法會從文檔中拿出快照并插入歷史書,并于每個暴露給客戶端作者的操作方法內被調用,做好歷史的傳承。最后我們加入第42行的撤銷操作,讓時間點回溯一個單位并恢復此處的快照至文檔。當編輯器擁有了撤銷功能后,我們的鬼才作者將高枕無憂的去倒咖啡了。
public class Author {public static void main(String[] args) {Editor editor = new Editor(new Doc("《AI的覺醒》"));/*<<<打開文檔《AI的覺醒》文章結束>>>*/editor.append("第一章 混沌初開");/*<<<插入操作第一章 混沌初開文章結束>>>*/editor.append("n 正文2000字……");/*<<<插入操作第一章 混沌初開正文2000字……文章結束>>>*/editor.append("n第二章 荒漠之花n 正文3000字……");/*<<<插入操作第一章 混沌初開正文2000字……第二章 荒漠之花正文3000字……文章結束>>>*/editor.delete();/*<<<刪除操作文章結束>>> *///吃下后悔藥,我的世界又完整了。editor.undo();/*>>>撤銷操作第一章 混沌初開正文2000字……第二章 荒漠之花正文3000字……文章結束>>>*/} }
可以看到,熊孩子做了delete操作后,作者輕松淡定地按下了Ctr+z,一切恢復如初,世界依舊美好,挽回那逝去的青蔥歲月。當然,代碼中我們略去了一些功能,比如讀者還可以加入重做redo操作,彈指之間,讓歷史在時間軸上來去自如,我的電腦我做主,時空穿梭,逆天之做。
誠然,任何模式都有其優缺點,備忘錄雖然看起來完美,但如果歷史狀態內容過大,會導致內存消耗嚴重,別忘了那邊歷史書的list是在內存中的哦,所以我們一定要依場景靈活運用,切不可生搬硬套。
轉載于:https://www.cnblogs.com/javazhiyin/p/9869002.html
總結
以上是生活随笔為你收集整理的设计模式是什么鬼(备忘录)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: VUE技术栈学习笔记(https://s
- 下一篇: 报表学习总结(一)——ASP.NET 水