让软件系统能够回到误操作前的动静,若是大家想重操旧业对象的气象澳门威尼斯人网址

情势动机
澳门威尼斯人网址 1

【学习难度:★★☆☆☆,使用效用:★★☆☆☆】
一直出处:http://woquanke.com/books/gof/
梳理和读书:https://github.com/BruceOuyang/boy-design-pattern
简书日期: 2018/03/26
简书首页:https://www.jianshu.com/p/0fb891a7c5ed

一、引言

为了使软件的选用更为人性化,对于误操作,大家必要提供一种类似“后悔药”的编写制定,让软件系统能够重回误操作前的景况,由此要求保留用户每三次操作时系统的事态,一旦出现误操作,能够把仓库储存的野史场所取出即可回到以前的情况。
现行反革命大多数软件都有撤除(Undo)的效应,快速键一般都以Ctrl+Z,目标正是为着化解那几个后悔的标题。
澳门威尼斯人网址 2

收回效能的兑现——备忘录格局(一)

种种人都有过后悔的时候,但人生并无后悔药,有些错误一旦产生就不可能再挽回,有个别人借使错过就不会再回去,有些话一旦说说话就不只怕再撤消,那便是人生。为了不后悔,凡事大家都亟需深思远虑。说了这么多,我们可能早已晕了,不是在学设计情势吗?为啥弄出那般一堆人生顿悟来,呵呵,别着急,本章将介绍一种让大家得以在软件中实现后悔机制的设计格局——备忘录情势,它是软件中的“后悔药”,是软件中的“月光宝盒”。话不多说,下边就让大家进入备忘录形式的就学。

21.1 可悔棋的中夏族民共和国象棋

Sunny软件集团欲开发一款能够运转在Android平台的触摸式中华人民共和国象棋软件,由于考虑到稍微用户是“菜鸟”,平日非常大心走错棋;还有个别用户因为不习惯使用手指在手提式有线话机荧屏上拖动棋子,通常出现操作失误,因而该中中原人民共和国象棋软件要提供“悔棋”成效,用户走错棋或操作失误后可过来到前八个手续。如图21-1所示:

澳门威尼斯人网址 3

图21-1 Android版中夏族民共和国象棋软件界面示意图

什么样落到实处“悔棋”功用是Sunny软件商店开发职员须要面对的三个关键难点,“悔棋”正是让系统苏醒到有些历史气象,在不少软件中家常便饭号称“撤废”。下边我们来简单分析一下撤废成效的兑现原理:

在促成打消时,首先必须保留软件系统的历史情形,当用户要求废除错误操作并且再次回到到某些历史气象时,能够取出事先保存的历史图景来覆盖当前事态。如图21-2所示:

澳门威尼斯人网址 4

图21-2撤回功效示意图

备忘录形式正为消除此类裁撤难点而诞生,它为大家的软件提供了“后悔药”,通过使用备忘录情势能够使系统苏醒到某一特定的野史图景。

  
昨天我们开始讲“行为型”设计情势的第几个格局,该形式是【备忘录格局】,英文名称是:Memento
帕特tern。按规矩,先从名称上来探视那个形式,个人的最初知道正是对有些对象的景色举行保存,等到需求苏醒的时候,能够从备忘录中展开复原。生活中那样的事例也能时不时看看,如备份电话通信录,备份操作操作系统,备份数据库等。假诺我们想恢复生机对象的情事,那么大家或许首先想到的是把指标保存下来,然而这样会损坏对象的封装性。因为对象有事态有操作,假如大家为了保存对象而留着原来的对象,做四个深拷贝,那么别的对象也能通过那些目的的接口访问这些目的情况,那并不是我们所愿意的。而小编辈须要它的天职只是保存和苏醒对象情形,而不应在上边帮衬对指标情形访问的接口,那就时有发生了Memento形式。

在使用软件的费用进度中,很多时候我们都急需记录1个对象的里边情况。
在切实可行落到实处进度中,为了允许用户撤销不鲜明的操作或从漏洞百出中复苏过来,需求完成备份点和撤消机制,而要达成那几个机制,必须先行将景况消息保存在某处,那样才能将目的复苏到它们原来的意况。
备忘录格局是一种给咱们的软件提供后悔药的机制,通过它能够使系统苏醒到某一一定的野史场地。

收回功效的完毕——备忘录格局(二)

21.2 备忘录方式概述

备忘录格局提供了一种状态回升的兑现机制,使得用户可以一本万利地回到四个一定的野史步骤,当新的情况不行只怕存在难题时,能够行使权且储存起来的备忘录将气象苏醒,当前多如牛毛软件都提供了裁撤(Undo)操作,其中就应用了备忘录情势。

备忘录格局定义如下:

备忘录格局(Memento
Pattern):在不损坏封装的前提下,捕获2个指标的中间景色,并在该对象之外保存那一个情状,那样能够在事后将目标苏醒到原来保存的事态。它是一种对象行为型方式,其外号为Token。

备忘录格局的中坚是备忘录类以及用于管理备忘录的承受人类的设计,其结构如图21-3所示:

澳门威尼斯人网址 5

图21-3 备忘录情势结构图

在备忘录格局协会图中含有如下多少个剧中人物:

  • Originator(原发器):它是2个普通类,可以创设3个备忘录,并储存它的脚下里面景观,也足以应用备忘录来过来其中间景况,一般将供给保留内部景观的类设计为原发器。

  • Memento(备忘录):存储原发器的里边景观,依据原发器来支配封存哪些内部处境。备忘录的筹划一般能够参考原发器的规划,依照实际必要规定备忘录类中的属性。需求小心的是,除了原发器自个儿与担当人类之外,备忘录对象无法间接供其余类使用,原发器的布置性在分裂的编制程序语言中贯彻机制会迥然分裂。

  • Caretaker(总管):监护人又称为管理者,它承受保存备忘录,不过无法对备忘录的内容举办操作或检查。在背负人类中能够储存三个或多个备忘录对象,它只担负储存对象,而无法改改对象,也毫无知道对象的贯彻细节。

明白备忘录格局并简单,但关键在于怎么样统一筹划备忘录类和理事类。由于在备忘录中存款和储蓄的是原发器的中间状态,由此必要防患原发器以外的其余对象访问备忘录,尤其是差别意别的对象来修改备忘录。

上边大家经过不难的以身作则代码来验证什么行使Java语言达成备忘录方式:

在利用备忘录格局时,首先应当留存三个原发器类Originator,在真实工作中,原发器类是二个具体的业务类,它包蕴部分用以存款和储蓄成员数量的习性,典型代码如下所示:

package dp.memento;  
public class Originator {  
    private String state;  

    public Originator(){}  

  // 创建一个备忘录对象  
    public Memento createMemento() {  
    return new Memento(this);  
    }  

  // 根据备忘录对象恢复原发器状态  
    public void restoreMemento(Memento m) {  
     state = m.state;  
    }  

    public void setState(String state) {  
        this.state=state;  
    }  

    public String getState() {  
        return this.state;  
    }  
}

对于备忘录类Memento而言,它经常提供了与原发器相对应的特性(能够是总体,也能够是部分)用于存储原发器的气象,典型的备忘录类设计代码如下:

package dp.memento;  
//备忘录类,默认可见性,包内可见  
class Memento {  
    private String state;  

    public Memento(Originator o) {  
    state = o.getState();  
    }  

    public void setState(String state) {  
        this.state=state;  
    }  

    public String getState() {  
        return this.state;  
    }  
}

在筹划备忘录类时必要考虑其封装性,除了Originator类,不允许任何类来调用备忘录类Memento的构造函数与有关办法,假若不考虑封装性,允许任何类调用setState()等方法,将导致在备忘录中保留的野史场所产生变更,通过撤废操作所苏醒的动静就不再是实在的野史图景,备忘录情势也就失去了本身的意思。

在选择Java语言落成备忘录方式时,一般通过将Memento类与Originator类定义在同一个包(package)中来落到实处封装,在Java语言中可选取私下认可访问标识符来定义Memento类,即确定保证其包内可知。唯有Originator类能够对Memento举办访问,而限制了其余类对Memento的拜会。在
Memento中保留了Originator的state值,假若Originator中的state值改变现在需裁撤,可以由此调用它的restoreMemento()方法开始展览恢复生机。

对于承担人类Caretaker,它用来保存备忘录对象,并提供getMemento()方法用于向客户端重返一个备忘录对象,原发器通过应用这么些备忘录对象能够回去有个别历史气象。典型的承担人类的落到实处代码如下:

package dp.memento;  
public class Caretaker {  
    private Memento memento;  

    public Memento getMemento() {  
        return memento;  
    }  

    public void setMemento(Memento memento) {  
        this.memento=memento;  
    }  
}

在Caretaker类中不应该一直调用Memento中的状态改变方法,它的功能只是用于存款和储蓄备忘录对象。将原发器备份生成的备忘录对象存款和储蓄在里面,当用户须要对原发器实行理并答复苏时再将积存在当中的备忘录对象取出。

思考

能还是不可能通过原型格局来创设备忘录对象?系统该怎么筹划?

        澳门威尼斯人网址 6

情势定义
备忘录方式(Memento
Pattern):在不破坏封装的前提下,捕获三个对象的里边处境,并在该指标之外保存那么些情况,那样能够在现在将对象恢复生机到原来保存的场合。它是一种对象行为型方式,其别称为Token。
Memento Pattern: Without violating encapsulation, capture and
externalize an object’s internal state so that the object can be
restored to this state later.
Frequency of use: medium low
UML图
澳门威尼斯人网址 7

裁撤成效的兑现——备忘录情势(三)

21.3 完整消除方案

为了贯彻撤废作用,Sunny公司开发人士决定利用备忘录模式来安排中夏族民共和国象棋软件,其宗旨协会如图21-4所示:

澳门威尼斯人网址 8

图21-4中夏族民共和国象棋棋子撤废功用结构图

在图21-4中,Chessman充当原发器,ChessmanMemento充当备忘录,MementoCaretaker充当理事,在MementoCaretaker中定义了七个ChessmanMemento类型的对象,用于存款和储蓄备忘录。完整代码如下所示:

//象棋棋子类:原发器  
class Chessman {  
    private String label;  
    private int x;  
    private int y;  

    public Chessman(String label,int x,int y) {  
        this.label = label;  
        this.x = x;  
        this.y = y;  
    }  

    public void setLabel(String label) {  
        this.label = label;   
    }  

    public void setX(int x) {  
        this.x = x;   
    }  

    public void setY(int y) {  
        this.y = y;   
    }  

    public String getLabel() {  
        return (this.label);   
    }  

    public int getX() {  
        return (this.x);   
    }  

    public int getY() {  
        return (this.y);   
    }  

    //保存状态  
    public ChessmanMemento save() {  
        return new ChessmanMemento(this.label,this.x,this.y);  
    }  

    //恢复状态  
    public void restore(ChessmanMemento memento) {  
        this.label = memento.getLabel();  
        this.x = memento.getX();  
        this.y = memento.getY();  
    }  
}  

//象棋棋子备忘录类:备忘录  
class ChessmanMemento {  
    private String label;  
    private int x;  
    private int y;  

    public ChessmanMemento(String label,int x,int y) {  
        this.label = label;  
        this.x = x;  
        this.y = y;  
    }  

    public void setLabel(String label) {  
        this.label = label;   
    }  

    public void setX(int x) {  
        this.x = x;   
    }  

    public void setY(int y) {  
        this.y = y;   
    }  

    public String getLabel() {  
        return (this.label);   
    }  

    public int getX() {  
        return (this.x);   
    }  

    public int getY() {  
        return (this.y);   
    }     
}  

//象棋棋子备忘录管理类:负责人  
class MementoCaretaker {  
    private ChessmanMemento memento;  

    public ChessmanMemento getMemento() {  
        return memento;  
    }  

    public void setMemento(ChessmanMemento memento) {  
        this.memento = memento;  
    }  
}  

编写如下客户端测试代码:

class Client {  
    public static void main(String args[]) {  
        MementoCaretaker mc = new MementoCaretaker();  
        Chessman chess = new Chessman("车",1,1);  
        display(chess);  
        mc.setMemento(chess.save()); //保存状态       
        chess.setY(4);  
        display(chess);  
        mc.setMemento(chess.save()); //保存状态  
        display(chess);  
        chess.setX(5);  
        display(chess);  
        System.out.println("******悔棋******");     
        chess.restore(mc.getMemento()); //恢复状态  
        display(chess);  
    }  

    public static void display(Chessman chess) {  
        System.out.println("棋子" + chess.getLabel() + "当前位置为:" + "第" + chess.getX() + "行" + "第" + chess.getY() + "列。");  
    }  
}

编写翻译并运行程序,输出结果如下:

棋子车当前位置为:第1行第1列。
棋子车当前位置为:第1行第4列。
棋子车当前位置为:第1行第4列。
棋子车当前位置为:第5行第4列。
******悔棋******
棋子车当前位置为:第1行第4列。

    
大家看上海教室,3个对象自然会有过多意况,那一个景况必然会互相转变而拉动指标的上扬,假若要想在某一每16日把当前目的回复到之前某一整日的情况,那一个场所用“备忘录形式”就能很好解决该难题。

格局结构
备忘录情势涵盖如下剧中人物:
Originator: 原发器
Memento: 备忘录
Caretaker: 负责人

撤废效率的兑现——备忘录格局(四)

21.4 达成数十二遍打消

Sunny软件集团开发人员通过选用备忘录格局达成了中华夏族民共和国象棋棋子的吊销操作,不过使用上述代码只可以促成一回吊销,因为在管事人类中只定义三个备忘录对象来保存情状,后边保存的意况会将前一回保存的情况覆盖,但有时用户须要打消多步操作。怎么着贯彻数十次注销呢?本节将提供一种多次撤回的解决方案,那正是在背负人类中定义一个集聚来存储五个备忘录,每种备忘录负责保存2个历史气象,在撤销时得以对备忘录集合进行逆向遍历,回到三个点名的历史景况,而且还足以对备忘录集合实行正向遍历,完成重做(Redo)操作,即撤销撤除,让对象情状获得上涨。

改正之后的中中原人民共和国象棋棋子裁撤功效布局图如图21-5所示:

澳门威尼斯人网址 9

图21-5革新之后的中夏族民共和国象棋棋子打消作用结构图

在图21-5中,我们对理事类MementoCaretaker进行了修改,在其间定义了二个ArrayList类型的联谊对象来储存七个备忘录,其代码如下所示:

import java.util.*;  

class MementoCaretaker {  
    //定义一个集合来存储多个备忘录  
    private ArrayList mementolist = new ArrayList();  

    public ChessmanMemento getMemento(int i) {  
        return (ChessmanMemento)mementolist.get(i);  
    }  

    public void setMemento(ChessmanMemento memento) {  
        mementolist.add(memento);  
    }  
}

编写如下客户端测试代码:

class Client {  
private static int index = -1; //定义一个索引来记录当前状态所在位置  
    private static MementoCaretaker mc = new MementoCaretaker();  

    public static void main(String args[]) {  
        Chessman chess = new Chessman("车",1,1);  
        play(chess);          
        chess.setY(4);  
        play(chess);  
        chess.setX(5);  
        play(chess);      
        undo(chess,index);  
        undo(chess,index);    
        redo(chess,index);  
        redo(chess,index);  
    }  

    //下棋  
    public static void play(Chessman chess) {  
        mc.setMemento(chess.save()); //保存备忘录  
        index ++;   
        System.out.println("棋子" + chess.getLabel() + "当前位置为:" + "第" + chess.getX() + "行" + "第" + chess.getY() + "列。");  
    }  

    //悔棋  
    public static void undo(Chessman chess,int i) {  
        System.out.println("******悔棋******");  
        index --;   
        chess.restore(mc.getMemento(i-1)); //撤销到上一个备忘录  
        System.out.println("棋子" + chess.getLabel() + "当前位置为:" + "第" + chess.getX() + "行" + "第" + chess.getY() + "列。");  
    }  

    //撤销悔棋  
    public static void redo(Chessman chess,int i) {  
        System.out.println("******撤销悔棋******");   
        index ++;   
        chess.restore(mc.getMemento(i+1)); //恢复到下一个备忘录  
        System.out.println("棋子" + chess.getLabel() + "当前位置为:" + "第" + chess.getX() + "行" + "第" + chess.getY() + "列。");  
    }  
}

编写翻译并运转程序,输出结果如下:

棋子车当前位置为:第1行第1列。
棋子车当前位置为:第1行第4列。
棋子车当前位置为:第5行第4列。
******悔棋******
棋子车当前位置为:第1行第4列。
******悔棋******
棋子车当前位置为:第1行第1列。
******撤销悔棋******
棋子车当前位置为:第1行第4列。
******撤销悔棋******
棋子车当前位置为:第5行第4列。

扩展

本实例只可以促成最简单易行的Undo和Redo操作,并未考虑对象情形在操作进程中冒出分支的情景。假使在裁撤到某些历史图景之后,用户再修改对象意况,此后执行Undo操作时或然会发出对象意况错误,大家能够考虑其发出原因。【注:可将目的景况的变动绘制成一张树状图进行分析。】

在骨子里支出中,能够利用链表也许仓库来处理有分支的靶子情状改变,大家可通过链表只怕仓库对上述实例进行革新。

二 、备忘录方式的详尽介绍

模式分析
鉴于在备忘录中存款和储蓄的是原发器的中间状态,因此须求防备原发器以外的任何对象访问备忘录。
备忘录对象平日封装了原发器的一部分或有所的气象音信,而且那一个情状无法被其它对象访问,也正是说无法在备忘录对象之外保存原发器状态,因为暴光其内部景色将违反封装的口径,大概有损系统的可相信性和可扩大性。

撤除效率的贯彻——备忘录格局(五)

21.5 再谈备忘录的卷入

备忘录是三个很卓绝的对象,唯有原发器对它抱有决定的权限,总管只负责管理,而任何类不可能访问到备忘录,因而我们供给对备忘录实行打包。

为了贯彻对备忘录对象的包裹,必要对备忘录的调用实行支配,对于原发器而言,它能够调用备忘录的兼具新闻,允许原发器访问归来到在此之前意况所需的保有数据;对于官员而言,只承担备忘录的保存并将备忘录传递给其余对象;对于任何对象而言,只须求从领导处取出备忘录对象并将原发器对象的处境上涨,而无须关心备忘录的保存细节。理想的状态是只同意生成该备忘录的丰富原发器访问备忘录的其中意况。

在事实上开发中,原发器与备忘录之间的关系是十三分非凡的,它们要享用消息而不让其余类知道,达成的方式因编制程序语言的不比而持有出入,在C++中能够利用friend关键字,让原发器类和备忘录类成为友元类,互相之间能够访问对象的片段私人住房的性质;在Java语言中得以将原发器类和备忘录类放在2个包中,让它们中间知足暗中同意的包内可知性,也能够将备忘录类作为原发器类的内部类,使得唯有原发器才能够访问备忘录中的数据,别的对象都爱莫能助运用备忘录中的数据。

思考

什么样利用个中类来达成备忘录形式?

21.6 备忘录方式总计

备忘录情势在众多软件的应用进程中普遍存在,不过在利用软件开发中,它的选拔成效并不太高,因为前几天游人如织依据窗体和浏览器的运用软件并不曾提供撤废操作。要是供给为软件提供撤除作用,备忘录情势无疑是一种很好的解决方案。在一部分字处理软件、图像编辑软件、数据库管理种类等软件中备忘录方式都取得了很好的利用。

1.首要优点

备忘录格局的重点优点如下:

(1)它提供了一种情况上涨的兑现机制,使得用户能够方便地回去多少个一定的野史步骤,当新的气象不行可能存在难点时,能够应用暂且储存起来的备忘录将情形复苏。

(2)备忘录完结了对消息的包裹,二个备忘录对象是一种原发器对象情状的表示,不会被其余轮代理公司码所改变。备忘录保存了原发器的情景,采取列表、堆栈等联谊来囤积备忘录对象能够达成数十次注销操作。

2.重中之重缺点

备忘录格局的要紧弱点如下:

能源消耗过大,若是急需保留的原发器类的分子变量太多,就不可制止需求占用大批量的储存空间,每保存二次对象的意况都亟需开支一定的系统能源。

3.适用场景

在以下情状下得以考虑采取备忘录形式:

(1)保存3个指标在某三个每十15日的凡事地方或一些境况,那样之后要求时它亦可东山再起到在此之前的景况,达成打消操作。

(2)制止外界对象破坏3个目的历史气象的封装性,制止将对象历史图景的落到实处细节暴光给外界对象。

练习

Sunny软件商店正在开发一款RAV4PG网游,为了给玩家提供越来越多造福,在游戏进度中能够设置二个苏醒点,用于保存当前的玩乐场景,假若在后续游戏经过中玩家剧中人物“不幸捐躯”,能够再次来到到以前保存的景色,从所设恢复点起来重新游戏。试使用备忘录形式设计该意义。

勤学苦练会在我的github上做掉

2.1、动机(Motivate)

为了兑现对备忘录对象的卷入,需求对备忘录的调用举办支配:

对于原发器而言,它可以调用备忘录的享有音信,允许原发器访问回到到从前景观所需的具备数据;
 对于官员而言,只负责备忘录的保留并将备忘录传递给其余对象;

对于别的对象而言,只供给从官员处取出备忘录对象并将原发器对象的动静上升,而无须关注备忘录的保留细节。
精美的情况是只允许生成该备忘录的尤其原发器访问备忘录的中间景色。

  
在软件营造进程中,有个别对象的地方在转换的进程中,可能由于某种供给,要求程序能够回溯到对象以前处于有个别点时的景观。要是运用部分国有接口来让任何对象获得指标的情形,便会揭示指标的底细达成。

格局实例与分析
游玩存进程—备忘录形式示例代码
系统布局
澳门威尼斯人网址 10

 
怎么着兑现目的情形的可观保存与回复,但还要又不会由此而损坏对象自笔者的封装性?

Originator: 原发器 GameRole.cs

2.2、意图(Intent)

using System;

namespace MementoPattern
{
    class GameRole
    {
        //生命力
        private int vit;
        public int Vitality
        {
            get { return vit; }
            set { vit = value; }
        }
        //攻击力
        private int atk;
        public int Attack
        {
            get { return atk; }
            set { atk = value; }
        }
        //防御力
        private int def;
        public int Defense
        {
            get { return def; }
            set { def = value; }
        }
        //状态显示
        public void StateDisplay()
        {
            Console.WriteLine("角色当前状态:");
            Console.WriteLine("体力:{0}", this.vit);
            Console.WriteLine("攻击力:{0}", this.atk);
            Console.WriteLine("防御力:{0}", this.def);
            Console.WriteLine("");
        }
        //获得初始状态
        public void GetInitState()
        {
            this.vit = 100;
            this.atk = 100;
            this.def = 100;
        }
        //战斗
        public void Fight()
        {
            this.vit = 0;
            this.atk = 0;
            this.def = 0;
        }
        //保存角色状态
        public RoleStateMemento SaveState()
        {
            return (new RoleStateMemento(vit, atk, def));
        }
        //恢复角色状态
        public void RecoveryState(RoleStateMemento memento)
        {
            this.vit = memento.Vitality;
            this.atk = memento.Attack;
            this.def = memento.Defense;
        }
    }
}

  
在不损坏封装性的前提下,捕获三个指标的中间景况,并在该对象之外保存这几个场地(倘使没有这么些关键点,其实深拷贝就足以化解难点)。那样现在就能够将该对象复苏到原来保存的图景。                                     
——《设计格局》GoF

Memento: 备忘录 RoleStateMemento.cs

2.3、结构图(Structure)

namespace MementoPattern
{
    //角色状态存储箱
    class RoleStateMemento
    {
        private int vit;
        private int atk;
        private int def;
        public RoleStateMemento(int vit, int atk, int def)
        {
            this.vit = vit;
            this.atk = atk;
            this.def = def;
        }
        //生命力
        public int Vitality
        {
            get { return vit; }
            set { vit = value; }
        }
        //攻击力
        public int Attack
        {
            get { return atk; }
            set { atk = value; }
        }
        //防御力
        public int Defense
        {
            get { return def; }
            set { def = value; }
        }
    }
}

       澳门威尼斯人网址 11

Caretaker: 负责人 RoleStateCaretaker.cs

2.④ 、方式的结合
    
    能够见到,在备忘录方式的构造图有以下剧中人物:
 
   
(1)、发起人剧中人物(Originator):笔录当前时时的中间情状,负责成立和回复备忘录数据。负责创设二个备忘录Memento,用以记录当前时刻本人的在那之中情况,并可采纳备忘录恢复内部景象。Originator【发起人】能够根据须要控制Memento【备忘录】存款和储蓄自身的怎样内部景色。

namespace MementoPattern
{
    //角色状态管理者
    class RoleStateCaretaker
    {
        private RoleStateMemento memento;
        public RoleStateMemento Memento
        {
            get { return memento; }
            set { memento = value; }
        }
    }
}

   
(2)、备忘录剧中人物(Memento):担当储存发起人对象的里边情状,在进展复原时提须要发起人须求的景况,并得避防患Originator以外的别的对象访问备忘录。备忘录有几个接口:Caretaker【管理剧中人物】只可以见到备忘录的窄接口,他只可以将备忘录传递给任何对象。Originator【发起人】却可阅览备忘录的宽接口,允许它访问归来到以前景况所急需的有着数据。

Client:客户类

   
(3)、管理者剧中人物(Caretaker):顶住保存备忘录对象。负责备忘录Memento,不能够对Memento的内容进行走访依然操作。

using System;

namespace MementoPattern
{
    class Program
    {
        static void Main(string[] args)
        {
            //大战Boss前
            GameRole lixiaoyao = new GameRole();
            lixiaoyao.GetInitState();
            lixiaoyao.StateDisplay();

            //保存进度
            RoleStateCaretaker stateAdmin = new RoleStateCaretaker();
            stateAdmin.Memento = lixiaoyao.SaveState();

            //大战Boss时,损耗严重
            lixiaoyao.Fight();
            lixiaoyao.StateDisplay();

            //恢复之前的状态
            lixiaoyao.RecoveryState(stateAdmin.Memento);
            lixiaoyao.StateDisplay();
            Console.Read();
        }
    }
}

2.五 、备忘录情势的代码完毕

情势优缺点
备忘录格局的亮点

提供了一种意况回涨的落到实处机制,使得用户能够方便地回到一个一定的野史步骤,当新的意况不行恐怕存在难题时,能够采纳之前囤积起来的备忘录将状态苏醒。

完毕了音讯的包裹,一个备忘录对象是一种原发器对象的代表,不会被此外代码改动,那种形式简化了原发器对象,备忘录只保留原发器的图景,接纳堆栈来囤积备忘录对象能够实现数十次注销操作,可以因而在主任中定义集合对象来储存七个备忘录。
备忘录情势的缺点

财富消耗过大,如若类的成员变量太多,就不可防止占用多量的内部存款和储蓄器,而且每保存2次对象的景色都急需成本内部存款和储蓄器财富,即便精晓那点大家就简单掌握为啥有个别提供了收回成效的软件在运作时所需的内存和硬盘空间比较大了。

    今日大家就用备份电话本的实例来表达备忘录情势的落到实处。达成代码如下:
  

形式适用条件
在以下情状下能够应用备忘录情势:

保存一个对象在某叁个随时的情事或部分景况,那样之后要求时它能够过来到在此之前的境况。

假如用贰个接口来让任何对象得到这一个情况,将会揭露目的的兑现细节并破坏对象的封装性,1个对象不指望外面直接待上访问其里面景色,通过领导能够直接访问其内部意况。

  1 namespace MementoPattern
  2 {
  3     // 联系人--需要备份的数据,是状态数据,没有操作
  4     public sealed class ContactPerson
  5     {
  6         //姓名
  7         public string Name { get; set; }
  8 
  9         //电话号码
 10         public string MobileNumber { get; set; }
 11     }
 12  
 13     // 发起人--相当于【发起人角色】Originator
 14     public sealed class MobileBackOriginator
 15     {
 16         // 发起人需要保存的内部状态
 17         private List<ContactPerson> _personList;
 18 
 19         
 20         public List<ContactPerson> ContactPersonList
 21         {
 22            get
 23            {
 24               return this._personList;
 25             }
 26 
 27            set
 28            {
 29               this._personList=value;
 30            }
 31         }
 32         //初始化需要备份的电话名单
 33         public MobileBackOriginator(List<ContactPerson> personList)
 34         {
 35             if(personList!=null)
 36             {
 37                 this._personList = personList;
 38              }
 39             else
 40             {
 41                throw new ArgumentNullException("参数不能为空!");
 42              }
 43         }
 44  
 45         // 创建备忘录对象实例,将当期要保存的联系人列表保存到备忘录对象中
 46         public ContactPersonMemento CreateMemento()
 47         {
 48             return new ContactPersonMemento(new List<ContactPerson>(this._personList));
 49         }
 50  
 51         // 将备忘录中的数据备份还原到联系人列表中
 52         public void RestoreMemento(ContactPersonMemento memento)
 53         {
 54             this.ContactPersonList = memento.ContactPersonListBack;
 55         }
 56  
 57         public void Show()
 58         {
 59             Console.WriteLine("联系人列表中共有{0}个人,他们是:", ContactPersonList.Count);
 60             foreach (ContactPerson p in ContactPersonList)
 61             {
 62                 Console.WriteLine("姓名: {0} 号码: {1}", p.Name, p.MobileNumber);
 63             }
 64         }
 65     }
 66  
 67     // 备忘录对象,用于保存状态数据,保存的是当时对象具体状态数据--相当于【备忘录角色】Memeto
 68     public sealed class ContactPersonMemento
 69     {
 70         // 保存发起人创建的电话名单数据,就是所谓的状态
 71         public List<ContactPerson> ContactPersonListBack{get;private set;};
 72  
 73         public ContactMemento(List<ContactPerson> personList)
 74         {
 75             ContactPersonListBack = personList;
 76         }
 77     }
 78  
 79     // 管理角色,它可以管理【备忘录】对象,如果是保存多个【备忘录】对象,当然可以对保存的对象进行增、删等管理处理---相当于【管理者角色】Caretaker
 80     public sealed class MementoManager
 81     {
 82         //如果想保存多个【备忘录】对象,可以通过字典或者堆栈来保存,堆栈对象可以反映保存对象的先后顺序
 83         //比如:public Dictionary<string, ContactPersonMemento> ContactPersonMementoDictionary { get; set; }
 84         public ContactPersonMemento ContactPersonMemento{ get; set; }
 85     }
 86  
 87     class Program
 88     {
 89         static void Main(string[] args)
 90         {
 91             List<ContactPerson> persons = new List<ContactPerson>()
 92             {
 93                 new ContactPerson() { Name="黄飞鸿", MobileNum = "13533332222"},
 94                 new ContactPerson() { Name="方世玉", MobileNum = "13966554433"},
 95                 new ContactPerson() { Name="洪熙官", MobileNum = "13198765544"}
 96             };
 97 
 98             //手机名单发起人
 99             MobileBackOriginator mobileOriginator = new MobileBackOriginator(persons);
100             mobileOriginator.Show();
101  
102             // 创建备忘录并保存备忘录对象
103             MementoManager manager = new MementoManager();
104             manager.ContactPersonMemento = mobileOriginator.CreateMemento();
105  
106             // 更改发起人联系人列表
107             Console.WriteLine("----移除最后一个联系人--------");
108             mobileOriginator.ContactPersonList.RemoveAt(2);
109             mobileOriginator.Show();
110  
111             // 恢复到原始状态
112             Console.WriteLine("-------恢复联系人列表------");
113             mobileOriginator.RestoreMemento(manager.ContactPersonMemento);
114             mobileOriginator.Show();
115  
116             Console.Read();
117         }
118     }
119 }

方式选择
(1)
大概全体的文字或许图像编辑软件都提供了打消(Ctrl+Z)的效应,即撤除操作,不过当软件关闭再打开时不可能再实行打消操作,也等于说不能够再回来关闭软件前的意况,实际上那中间就动用到了备忘录方式,在编排文件的同时能够保存一些里头景观,那个情状在软件关闭时从内部存款和储蓄器销毁,当然那个情形的保留也不是最好的,很多软件只提供简单次的吊销操作。
(2)
数据库管理种类DBMS所提供的事务管理应用了备忘录方式,当数据库某工作中一条数据操作语句执行破产时,整个事情将拓展回滚操作,系统回到事情执行在此以前的事态。

三 、备忘录方式的实现中心:

情势增加
备忘录的封装性

为了保证备忘录的封装性,除了原发器外,其余类是不能够也不应有访问备忘录类的,在其实支出中,原发器与备忘录之间的涉嫌是尤其例外的,它们要分享新闻而不让其余类知道,达成的点子因编制程序语言的例外而不相同。

C++能够用friend关键字,使原发器类和备忘录类成为友元类,相互之间能够访问对象的有个别私有的习性;

在Java语言中得以将多少个类位居贰个包中,使它们中间满意私下认可的包内可知性,也得以将备忘录类作为原发器类的中间类,使得唯有原发器才足以访问备忘录中的数据,别的对象都无法儿采纳备忘录中的数据。
多备份达成

在首长中定义二个聚众对象来囤积两个情景,而且能够一本万利地回到到某一历史意况。
 在备份对象时能够做一些符号,这么些标记称为检查点(Check
Point)。在选用HashMap等实现时方可运用Key来安装检查点。

       
备忘录(Memento)存款和储蓄原发器(Originator)对象的内部景色,在供给时上涨原发器状态。Memento情势适用于“由原发器管理,却又不可能不存款和储蓄在原发器之外的新闻”。

 

  在落到实处Memento格局中,要谨防原发器以外的目的访问备忘录对象。备忘录对象有四个接口,二个为原发器使用的宽接口;1个为其余对象使用的窄接口。在落到实处Memento方式时,要考虑拷贝对象情状的功用问题,要是指标耗费相比较大,能够运用某种增量式改变(即只记住改变的情事)来立异Memento情势。

【注脚与感激】
正文,站在不少壮汉的肩膀上,借鉴和引用了好多别人拥有版权的创作或撰文,在此,对前人们的孝敬致谢。并还要发布引用的内容、原著者或缘于(一些源于网络的剧情作者无法追述本源,深表遗憾)。

  大家也得以用体系化的主意贯彻备忘录。类别化之后,我们能够把它权且保存到数据库、文件、进程内、进度外等地方。

【参考文献】
《设计格局—可复用面向对象软件的基本功》作者: [美] 埃里克h Gamma / RichardHelm / 拉尔夫 Johnson / John Vlissides 译者: 李英军 / 马晓星 / 蔡敏 /
刘建中 等 机械工业出版社
《重构—改进既有代码的宏图》小编: 马丁 Fowler译者:候捷
中夏族民共和国电力出版社
《敏捷软件开发—原则、情势与实践》小编: 罗伯特 C. 马丁 浙大东军事和政院学出版社
《程序员修炼之道—从小工到我们》我: Andrew Hunt / 大卫 Thomas电子工业出版社
《Head First 设计方式》小编: Freeman 译者: O’Reilly Taiwan集团中国电力出版社
《设计形式之禅》 小编: 秦小波 机械工业出版社
MSDN WebCast 《C#面向对象设计情势纵横谈》 教师:李建忠
刘伟同志. 设计形式. 新加坡:浙大东军政高校学出版社, 2012.
刘伟(Liu-Wei). 设计方式实验和培养和演练教程. 香岛:南开东军事和政院学出版社, 二零一二.
《大话设计方式》 作者: 程杰 浙大东军事和政院学出版社
《C#图解教程》小编: 索利斯 译者: 苏林 / 朱晔 人民邮政和邮电通信出版社
《你必须掌握的.NET》笔者: 王涛
《项目中的.NET》小编: 李天平 电子工业出版社
《Microsoft .NET集团级应用架构划设想计》作者: (美)埃斯波西托等撰写 译者:
陈黎夫
http://www.dofactory.com/Patterns/Patterns.aspx .NET Design Patterns
http://www.cnblogs.com/zhenyulu 博客小编:吕震宇
http://www.cnblogs.com/terrylee 博客小编:李会军
http://www.cnblogs.com/anlyren/ 博客小编:anlyren
http://www.cnblogs.com/idior 博客作者:idior
http://www.cnblogs.com/allenlooplee 博客作者:Allen lee
http://blog.csdn.net/ai92 博客小编:ai92
http://www.cnblogs.com/umlonline/ 博客小编:张传波
http://www.cnblogs.com/lovecherry/ 博客笔者:LoveCherry

    (1)、备忘录方式的要害优点有:

           
1】、假设某些操作不当地破坏了数量的完整性,此时得以选取备忘录情势将数据复苏成原本正确的多少。

           
2】、备份的地方数据保存在发起人剧中人物之外,那样发起人就不需求对各种备份的景色举办政管理制。而是由备忘录剧中人物举办田管,而备忘录剧中人物又是由领导剧中人物管理,符合单一职分规范。

           
3】、提供了一种情景恢复的实现机制,使得用户能够一本万利地回来八个特定的历史步骤,当新的气象不行可能存在难点时,能够使用在此以前囤积起来的备忘录将情形上涨。

           
4】、实现了消息的卷入,一个备忘录对象是一种原发器对象的代表,不会被其余代码改动,那种情势简化了原发器对象,备忘录只保留原发器的景况,采纳堆栈来储存备忘录对象足以兑现多次收回操作,能够通过在官员中定义集合对象来存款和储蓄五个备忘录。
  
           
5】、本方式简化了倡议人类。发起人不再须要管住和保存在那之中间情况的二个个本子,客户端能够自行保管他们所急需的那几个意况的版本。
  
           
6】、当发起人剧中人物的场馆改变的时候,有恐怕这几个情状不行,那时候就可以选用一时储存起来的备忘录将状态苏醒。

 (2)、备忘录方式的严重性缺点有:

           
1】、在骨子里的系统中,也许要求维护四个备份,须要额外的财富,那样对财富的成本相比较严重。财富消耗过大,假如类的分子变量太多,就不可幸免占用大批量的内部存款和储蓄器,而且每保存三遍对象的境况都亟需开销内部存储器财富,固然知道这一点大家就便于领会为何有的提供了撤消功能的软件在运维时所需的内存和硬盘空间相比较大了。

           
2】、若是发起人剧中人物的景色要求完整地蕴藏到备忘录对象中,那么在财富消耗上边备忘录对象会很高昂。

      3】、当领导剧中人物将1个备忘录
存款和储蓄起来的时候,理事或者并不知道那么些景况会占有多大的贮存空间,从而不能唤起用户二个操作是或不是很昂贵。

     
4】、当发起人剧中人物的境况改变的时候,有可能那么些体协会议无效。假如状态改变的成功率不高的话,不如使用“要是”协议格局。

    (3)、在底下的情况下得以设想使用备忘录方式:

         
1】、倘若系统须求提供回滚操作时,使用备忘录方式越发适宜。例如文本编辑器的Ctrl+Z撤消操作的贯彻,数据库广东中华工程集团作操作。

         
2】、保存1个目的在某二个随时的状态或一些景况,那样之后必要时它亦可还原到在此以前的意况。

         
3】、假诺用3个接口来让别的对象获得这一个情状,将会暴露指标的落成细节并破坏对象的封装性,3个目的不期望外界一向访问其里面境况,通过领导能够直接待上访问其内部景观。

         
4】、有时一些发起人对象的里边新闻必须保留在发起人对象以外的地点,然而必供给由发起人对象自身读取,那时,使用备忘录形式能够把复杂的建议者内部音讯对其余的靶子屏蔽起来,从而能够妥当地保持封装的界限。

    (4)备忘录的封装性

        
1】、为了确认保证备忘录的封装性,除了原发器外,其余类是不能够也不该访问备忘录类的,在实际开销中,原发器与备忘录之间的关系是非常格外的,它们要享受音信而不让别的类知道,完结的不二法门因编制程序语言的不等而各异。

    (5)多备份完结

        
1】、在领导中定义1个凑合对象来囤积七个状态,而且能够一本万利地回去到某一历史图景。

         2】、在备份对象时方可做一些标志,这一个标记称为检查点(Check
Point)。在行使HashMap等落到实处时得以动用Key来安装检查点。

肆 、.NET 备忘录方式的落实

    
在当今的Net框架之中,还未曾找到备忘录情势的贯彻,看来依然本人的造诣不够,还需努力。个人的精通,那种形式如同在工作体系里面使用的更加多,类似Word,Excel等工具得以有打消功效,其实过多软件都有这一个职能,软件推行的时候,时时刻刻在把团结的情形存款和储蓄,假使发生错误,或许须求裁撤的时候就能够拓展连锁的操作。

五、总结

    
备忘录形式写完了,那些格局刚开端理解起来仍然挺费力的,可是,如果大家多看多少个实例代码,完全控制也小难题。大家是否感觉Memento格局和Command格局有些类似,我们要仔细把握格局里面包车型客车异议,不然使用格局的时候就会并发指鹿为马的情事如故无法鲜明使用哪个情势好。Memento备忘录格局和Command命令情势其实依然有些轻微的差异的,那就让大家来看看她们的异议吧。尽管两者都帮忙Undo操作,然而Command是对行为的包裹,Memento是对指标景况的保留,那是指标上的不等。它们支持的也是Undo操作的不等范畴,Command是对行为类别的操作,Memento是对作为意况的操作。命令格局保存的是发起人的有血有肉命令(命令对应的是行为),而备忘录情势保存的是发起人的境况(而气象对应的数据结构,如属性)。把握细节,驾驭格局的行使场景,这样能够让情势更好的为大家服务。

相关文章