一、备忘录模式介绍

1. 解决的问题

次要解决在不毁坏封装性的前提下,捕捉一个对象的外部状态,并在该对象之外保留这个状态,从而能够复原原先保留的状态。

2. 定义

备忘录模式是一种行为设计模式,容许在不裸露对象实现细节的状态下保留和复原对象之前的状态。

3. 利用场景

  • 当你须要创建对象状态快照来复原其之前的状态时,能够应用备忘录模式。
  • 当间接拜访对象的成员变量、获取器或设置器将导致封装被冲破时,能够应用备忘录模式。

二、备忘录模式优缺点

1. 长处

  • 能够在不毁坏对象封装状况的前提下创建对象状态快照。
  • 能够通过让负责人保护原发器状态历史记录来简化原发器代码。

2. 毛病

  • 如果客户端过于频繁地创立备忘录,程序将耗费大量内存。
  • 负责人必须残缺跟踪原发器的生命周期,这样能力销毁弃用的备忘录。
  • 绝大部分动静编程语言(例如PHP、Python 和 JavaScript)不能确保备忘录中的状态不被批改。

三、备忘录模式利用实例:线上刚公布就出问题怎么办?

1. 实例场景

搬砖的咱们常常会遇到这个状况:

老代码出了 bug ,先看文档手册,我去,这代码连个文档都没有,只能看源码了。

看完一遍逻辑,再调试个半天,应该是这里出问题了,疾速批改结束,验证一遍,nice,没什么问题。

开始分割测试验证,测试环境验证通过!

激动人心的时刻来了:提交代码合并申请 -> 编译 -> 公布。

哎,怎么回事,线上公布的容器疯狂报错,起都起不来,我的天,怎么办?

旁边的共事:别慌,先把公布回滚吧,保障线上没问题!

明天,咱们就以公布回滚为例,介绍一下备忘录模式。

2. 备忘录模式实现

2.1 工程构造
└─ src    ├─ main    │    └─ java    │    └─ org.design.pattern.memento    │       └─ model    │            ├─ ReleaseRecord.java    │            ├─ ReleaseRecordOriginator.java    │            ├─ ReleaseRecordMemento.java    │            └─ ReleaseRecordCaretaker.javaa    └─ test        └─ java            └─ org.design.pattern.memento                  └─ MementoTest.java
2.2 代码实现
2.2.1 实体类

公布记录

/** * 公布记录 */@Getter@Setter@AllArgsConstructorpublic class ReleaseRecord {    /**     * 公布记录号     */    private Integer id;    /**     * 公布版本     */    private String version;    /**     * 公布容器     */    private String cloudName;    /**     * 开发     */    private String developerName;    /**     * 测试     */    private String testerName;}
2.2.2 备忘录模式相干类

公布记录原生器

/** * 公布记录原生器 */@Getter@Setterpublic class ReleaseRecordOriginator {    /**     * 公布记录     */    private ReleaseRecord releaseRecord;    /**     * 构造函数     *     * @param releaseRecord 公布记录     */    public ReleaseRecordOriginator(ReleaseRecord releaseRecord) {        this.releaseRecord = releaseRecord;    }    /**     * 保留公布记录     *     * @return ReleaseRecordMemento     */    public ReleaseRecordMemento save() {        return new ReleaseRecordMemento(this.releaseRecord);    }    /**     * 复原公布记录     *     * @param releaseRecordMemento 公布记录备忘录     */    public void restore(ReleaseRecordMemento releaseRecordMemento) {        this.releaseRecord = releaseRecordMemento.getReleaseRecord();    }}

公布记录备忘录

/** * 公布记录备忘录 */@Getter@Setterpublic class ReleaseRecordMemento {    /**     * 公布记录     */    private ReleaseRecord releaseRecord;    /**     * 构造函数     *     * @param releaseRecord 公布记录     */    public ReleaseRecordMemento(ReleaseRecord releaseRecord) {        this.releaseRecord = releaseRecord;    }}

公布记录负责人

/** * 公布记录负责人 */public class ReleaseRecordCaretaker {    /**     * 公布记录历史记录列表     */    private List<ReleaseRecordMemento> mementoList = new ArrayList<>();    /**     * 公布历史记录map(可依据公布记录id查问)     */    private Map<Integer, ReleaseRecordMemento> mementoMap = new HashMap<>();    /**     * 寄存公布记录     *     * @param releaseRecordMemento 公布记录备忘录     */    public void push(ReleaseRecordMemento releaseRecordMemento) {        mementoList.add(releaseRecordMemento);        mementoMap.put(releaseRecordMemento.getReleaseRecord().getId(), releaseRecordMemento);    }    /**     * 回滚     *     * @return ReleaseRecordMemento     */    public ReleaseRecordMemento undo() {        if (ObjectUtils.isEmpty(mementoList)) {            return null;        }        return mementoList.get(mementoList.size() - 1);    }    /**     * 依据公布记录id回滚     *     * @param releaseRecordId 公布记录id     * @return ReleaseRecordMemento     */    public ReleaseRecordMemento undo(Integer releaseRecordId) {        if (ObjectUtils.isEmpty(mementoMap) || ObjectUtils.isEmpty(mementoMap.get(releaseRecordId))) {            return null;        }        return mementoMap.get(releaseRecordId);    }}
2.3 测试验证
2.3.1 测试验证类
/*** * 备忘录模式测试 */@Slf4jpublic class MementoTest {    @Test    public void test() {        ReleaseRecordCaretaker recordCaretaker = new ReleaseRecordCaretaker();        ReleaseRecordOriginator recordOriginator = new ReleaseRecordOriginator(                new ReleaseRecord(1, "0.1.0", "test-cloud", "yiyu", "tester")        );        recordCaretaker.push(recordOriginator.save());        recordOriginator.setReleaseRecord(            new ReleaseRecord(2, "0.1.1", "test-cloud", "yiyu", "tester")        );        recordCaretaker.push(recordOriginator.save());        // 回滚        recordOriginator.restore(recordCaretaker.undo());        log.info("历史公布回滚:{}", JSON.toJSONString(recordOriginator.getReleaseRecord()));        // 依据公布id回滚        Integer undoReleaseRecordId = 1;        recordOriginator.restore(recordCaretaker.undo(undoReleaseRecordId));        log.info("回滚公布记录id为{}的公布:{}", undoReleaseRecordId, JSON.toJSONString(recordOriginator.getReleaseRecord()));    }}
2.3.2 测试后果
22:36:44.731 [main] INFO  o.d.pattern.memento.test.MementoTest - 历史公布回滚:{"cloudName":"test-cloud","developerName":"yiyu","id":2,"testerName":"tester","version":"0.1.1"}22:36:44.733 [main] INFO  o.d.pattern.memento.test.MementoTest - 回滚公布记录id为1的公布:{"cloudName":"test-cloud","developerName":"yiyu","id":1,"testerName":"tester","version":"0.1.0"}Process finished with exit code 0

四、备忘录模式构造

  1. 原发器(Originator)类能够生成本身状态的快照,也能够在须要时通过快照复原本身状态。
  2. 备忘录(Memento)是原发器状态快照的值对象(value object)。通常做法是将备忘录设为不可变的,并通过构造函数一次性传递数据。
  3. 负责人(Caretaker)仅晓得 “何时” 和 “为何” 捕获原发器的状态,以及何时复原状态。

    负责人通过保留备忘录栈来记录原发器的历史状态。当原发器须要回溯历史状态时,负责人将栈中获取最顶部的备忘录,并将其传递给原发器的复原(restoration)办法。

  4. 在实现办法中,备忘录类将被嵌套在原发器中。这样原发器就能够拜访备忘录的成员变量和办法,即便这些办法被申明为公有。另一方面,负责人对于备忘录的成员变量和办法的拜访权限十分无限:它们只能栈中保留备忘录,而不能批改其状态。

设计模式并不难学,其自身就是多年教训提炼出的开发指导思想,关键在于多加练习,带着应用设计模式的思维去优化代码,就能构建出更正当的代码。

源码地址:https://github.com/yiyufxst/design-pattern-java

参考资料:
小博哥重学设计模式:https://github.com/fuzhengwei/itstack-demo-design
深刻设计模式:[https://refactoringguru.cn/de...