Bootstrap

设计模式之 备忘录模式

备忘录模式是一种行为型设计模式,它允许你在不暴露对象实现细节的情况下,捕获和保存对象的内部状态。之后,可以通过保存的状态将对象恢复到原先的状态。备忘录模式的核心思想是“在不暴露对象的内部实现的情况下,保存对象的状态,并可以随时恢复这个状态”。

简单来说,备忘录模式帮助我们在某一时刻保存对象的状态,以便以后恢复。

一、备忘录模式的结构

备忘录模式由三个角色组成:

  1. Originator(发起人):负责创建备忘录和使用备忘录来恢复其内部状态。
  2. Memento(备忘录):备忘录对象包含了 Originator 的内部状态,可以在需要时提供给 Caretaker 使用。备忘录不应该对外暴露其内容。
  3. Caretaker(管理者):负责保存备忘录的对象,它不关心备忘录的内容,只知道如何保存和恢复备忘录。Caretaker 通常会持有多个备忘录,以便进行不同的状态恢复。

二、工作原理

备忘录模式的工作原理主要包括以下几个步骤:

  1. 保存状态:当对象的状态发生变化时,发起人对象(Originator)会创建一个备忘录对象(Memento),并将其当前状态存储在备忘录中。
  2. 恢复状态:当需要恢复对象的状态时,发起人会从管理者对象(Caretaker)处获取对应的备忘录,并使用备忘录中的状态信息恢复自身的状态。
  3. 管理备忘录:管理者对象(Caretaker)负责保存多个备忘录,并确保每个备忘录对应的对象状态是可以恢复的。

三、备忘录模式的实现示例

白箱备忘录

白箱备忘录模式是指备忘录对象暴露了内部状态的实现细节,管理者类(Caretaker)不仅能够保存和恢复备忘录,还可以直接访问备忘录中的数据。因此,备忘录是白箱的,它对外部类是透明的,管理者类可以直接读取和修改备忘录的内容。

  • 发起人类
public class Role {
    private int vit = 100;//生命
    private int atk = 100;//攻击
    private int def = 100;//防御

    public void fight(){
        this.vit = 0;
    }

    public Memento saveMemento(){
        return new Memento(vit,atk,def);
    }

    public void recoverMemento(Memento memento){
        this.vit = memento.getVit();
        this.atk = memento.getAtk();
        this.def = memento.getDef();
    }

    public void display(){
        System.out.println("生命力"+vit);
        System.out.println("攻击力"+atk);
        System.out.println("防御力"+def);
    }

}
  • 备忘录类
public class Memento {
    private int vit;
    private int atk;
    private int def;

    public int getVit() {
        return vit;
    }

    public int getAtk() {
        return atk;
    }

    public int getDef() {
        return def;
    }

    public Memento() {
    }

    public Memento(int vit, int atk, int def) {
        this.vit = vit;
        this.atk = atk;
        this.def = def;
    }
}
  • 管理者类
public class RoleStateCaretaker {
    private Memento memento;

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

    public Memento getRoleState(){
        return memento;
    }
}
  • 使用备忘录模式
    public class Client {
        public static void main(String[] args) {
            System.out.println("----------大战之前的状态--------");
            Role role = new Role();
            role.display();
    
            RoleStateCaretaker roleStateCaretaker = new RoleStateCaretaker();
            roleStateCaretaker.setRoleState(role.saveMemento());
    
            System.out.println("----------大战之后的状态--------");
            role.fight();
            role.display();
    
    
            System.out.println("----------恢复之前状态--------");
            role.recoverMemento(roleStateCaretaker.getRoleState());
            role.display();
        }
    }
  • 运行结果
黑箱备忘录 

黑箱备忘录模式是指备忘录对象完全封装了发起人的状态,外部类(包括管理者类 Caretaker)不能直接访问备忘录中的状态数据。换句话说,备忘录是黑箱的,发起人类是唯一能够读取和修改备忘录内部状态的类。管理者类(Caretaker)只负责保存和恢复备忘录对象,但无法查看备忘录中的数据内容。

针对上述示例,我们定义Memento接口供外部使用,Memento实现类为发起人类的私有内部类

  • 发起人类
    public class Role {
        private int vit = 100;//生命
        private int atk = 100;//攻击
        private int def = 100;//防御
    
    
    
        public void fight(){
            this.vit = 0;
        }
    
        public Memento saveMemento(){
    
            return new RoleStateMemento(vit,atk,def);
        }
    
        public void recoverMemento(Memento memento){
            RoleStateMemento roleStateMemento = (RoleStateMemento) memento;
            this.vit = roleStateMemento.getVit();
            this.atk = roleStateMemento.getAtk();
            this.def = roleStateMemento.getDef();
        }
    
        public void display(){
            System.out.println("生命力"+vit);
            System.out.println("攻击力"+atk);
            System.out.println("防御力"+def);
        }
    
        private class RoleStateMemento implements Memento {
            private int vit;
            private int atk;
            private int def;
    
            public int getVit() {
                return vit;
            }
    
            public int getAtk() {
                return atk;
            }
    
            public int getDef() {
                return def;
            }
    
    
            public RoleStateMemento(int vit, int atk, int def) {
                this.vit = vit;
                this.atk = atk;
                this.def = def;
            }
        }
    
    }
  • 标记接口
    public interface Memento {
    }
    
  • 管理员类
    public class RoleStateCaretaker {
        private Memento memento;
    
        public void setRoleState(Memento memento){
            this.memento = memento;
        }
    
        public Memento getRoleState(){
            return memento;
        }
    }
  • 测试类
    public class Client {
        public static void main(String[] args) {
            System.out.println("----------大战之前的状态--------");
            Role role = new Role();
            role.display();
    
            RoleStateCaretaker roleStateCaretaker = new RoleStateCaretaker();
            roleStateCaretaker.setRoleState(role.saveMemento());
    
            System.out.println("----------大战之后的状态--------");
            role.fight();
            role.display();
    
    
            System.out.println("----------恢复之前状态--------");
            role.recoverMemento(roleStateCaretaker.getRoleState());
            role.display();
        }
    }
  • 运行结果

四、备忘录模式的优缺点

优点:
  1. 封装性好:备忘录模式可以在不暴露对象内部实现细节的情况下,保存和恢复对象的状态,从而保持了封装性。
  2. 灵活性高:可以随时创建和恢复对象的不同状态,支持“撤销”和“恢复”功能。
  3. 实现简单:备忘录模式可以通过简单的对象组合实现,逻辑清晰且易于理解。
缺点:
  1. 内存消耗:如果每次都创建备忘录来保存对象的状态,会导致大量备忘录对象的产生,可能会占用较多内存。
  2. 复杂性增加:当对象的状态非常复杂时,备忘录的管理可能会变得很复杂,尤其是涉及多个状态的保存与恢复时。
  3. 可能会泄露实现细节:尽管备忘录对象的设计要求不能暴露状态信息,但在某些情况下,备忘录对象的实现可能会不小心暴露对象的内部状态。
五、适用场景
  1. 撤销操作:当需要实现操作的撤销功能时,备忘录模式是一个理想的选择。它可以在执行操作前保存状态,以便在撤销时恢复到先前的状态。
  2. 状态恢复:当需要恢复某一时刻的对象状态时,备忘录模式提供了便捷的解决方案。
  3. 版本管理:在版本控制系统中,保存每个版本的状态(如文件的内容或数据库的状态),然后可以随时恢复到某个历史版本。
;