备忘录模式(学习笔记)

  1. 意图

  在不破坏封装性的前提下,捕获一个对象的内部状态,并在对象之外保存这个状态。这样以后就可将该对象恢复到原先保存的状态

  2. 动机

   假如你开发一款文字编辑器应用程序。除了简单的文字编辑功能外,编辑器还要有设置文本格式和插入内嵌图片的等功能。后来,决定添加一个让用户能撤销施加在文本上的任何操作。

  刚开始,打算用直接的方式实现该功能:程序在执行任何操作前会记录所有对象的状态,并将其保存。当需要撤销某个操作时,程序将从历史记录中获得最近的快照,然后使用它来恢复所有对象的状态。

     

  但是,如何生成这些快照呢?先想到的是遍历对象的所有成员变量并将其数值复制保存。这需要对象本身没有严格的访问权限限制,但是,大多数对象会使用私有成员变量来存储重要数据,这样别人就无法轻易查看其中的内容。

  假设所有对象都是public的,这种方式,仍存在其他的问题。未来,由于需求变化,可能会添加或删除一些成员变量。这需要对负责复制对象状态的类进行修改。

  另外,为了让其他对象能保存或读取快照,很可能需要将快照的成员变量设为公有,而这将暴漏被复制对象的状态。其他类也会对快照类的每个小改动产生依赖。我们似乎走进了一个死胡同:要么暴漏类的所有内部细节而使其过去脆弱;要么限制对其状态的访问权限而无法生成快照。

  我们刚刚遇到的问题,都是封装破损造成的。一些对象试图超出其职责范围的工作。由于在执行某些行为时需要获取数据,所以它们侵入了其他对象的私有空间,而不是让这些对象来完成实际的工作

  备忘录模式将创建状态快照(Snapshot的工作委派给实际状态的拥有者原发器(Originator对象这样其他对象就不再需要从 “外部 复制编辑器状态了,编辑器类拥有其状态的完全访问权因此可以自行生成快照。模式建议将对象状态的副本存储在一个名为备忘录 (Memento 的特殊对象中 除了创建备忘录的对象外 任何对象都不能访问备忘录的内容 其他对象必须使用受限接口与备忘录进行交互 它们可以获取快照的元数据 (创建时间和操作名称等 但不能获取快照中原始对象的状态 

  3. 适用性

  • 必须保存一个对象在某个时刻的(部分)状态,这样以后需要时他才能恢复到先前的状态
  • 如果一个接口让其他对象直接得到这些状态,将会暴露对象的实现细节并破坏对象的封装性

  4. 结构

        

  5. 效果

  1) 可以在不破坏对象封装情况的前提下创建对象状态快照

  2) 简化了原发器   在其他的保持封装性的设计中,Originator负责保持客户请求过的内部状态版本。这就把所有存储管理的责任交给了Originator。让客户管理请求的状态可以简化Originator,并且使得客户工作结束时无需通知原发器

  3) 使用备忘录可能代价很高   如果原生器在生成备忘录时必须拷贝并存储大量的信息,或者客户非常频繁地创建备忘录和恢复原发器的状态,可能导致很大的开销。除非封装和恢复Originator状态的开销不大。

    -存储增量式改变    如果备忘录的创建及其返回的顺序是可预测的,备忘录可以仅存储原发器内部状态的增量改变

    例如,一个包含可撤销命令的历史列表可使用备忘录, 以保证命令被取消时他们可以恢复到正确的状态。历史列表定义了一个特定的顺序,按照这个顺序命令可以被撤销和重做。这意味着一个命令可以只存储一个命令所产生的增量改变而不是它所影响的每一个对象的完整状态。

  6. 代码实现    

  假设开发一个图形编辑器的撤销功能,其允许修改屏幕上形状的颜色和位置。但任何修改都可被撤销和重复。“撤销” 功能基于备忘录和命令模式的合作。编辑器记录命令的执行历史。在执行任何命令之前,都会生成备份并将其连接到一个命令对象。而在执行完成后,会将已执行的命令放入历史记录中。当用户请求撤销操作时,编辑器将从历史记录中获取最近的命令,恢复在该命令内部保存的状态备份。如果用户再次请求撤销操作,编辑器将恢复历史记录中的下一个命令,以此类推。被撤销的命令都将保存在历史记录中,直至用户对屏幕上的形状进行了修改。这对恢复被撤销的命令来说至关重要

  editor/Editor.java:编辑器代码

package memento.editor;

import memento.history.History;
import memento.history.Memento;
import command.commands.Command;
import composite.shapes.CompoundShape;
import memento.shapes.Shape;

import javax.swing.*;
import java.io.*;
import java.util.Base64;

/**
 * @author GaoMing
 * @date 2021/7/25 - 20:47
 */
public class Editor extends JComponent {
    private Canvas canvas;
    private CompoundShape allShapes = new CompoundShape();
    private History history;

    public Editor() {
        canvas = new Canvas(this);
        history = new History();
    }

    public void loadShapes(Shape... shapes) {
        allShapes.clear();
        allShapes.add(shapes);
        canvas.refresh();
    }

    public CompoundShape getShapes() {
        return allShapes;
    }

    public void execute(Command c) {
        history.push(c, new Memento(this));
        c.execute();
    }

    public void undo() {
        if (history.undo())
            canvas.repaint();
    }

    public void redo() {
        if (history.redo())
            canvas.repaint();
    }

    public String backup() {
        try {
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(baos);
            oos.writeObject(this.allShapes);
            oos.close();
            return Base64.getEncoder().encodeToString(baos.toByteArray());
        } catch (IOException e) {
            return "";
        }
    }

    public void restore(String state) {
        try {
            byte[] data = Base64.getDecoder().decode(state);
            ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(data));
            this.allShapes = (CompoundShape) ois.readObject();
            ois.close();
        } catch (ClassNotFoundException e) {
            System.out.print("ClassNotFoundException occurred.");
        } catch (IOException e) {
            System.out.print("IOException occurred.");
        }
    }
}

  editor/Canvas.java: 画布代码

package memento.editor;

import memento.commands.ColorCommand;
import memento.commands.MoveCommand;
import memento.shapes.Shape;

import javax.swing.*;
import javax.swing.border.Border;
import java.awt.*;
import java.awt.event.*;
import java.awt.image.BufferedImage;

/**
 * @author GaoMing
 * @date 2021/7/25 - 20:47
 */
public class Canvas extends java.awt.Canvas{
    private Editor editor;
    private JFrame frame;
    private static final int PADDING = 10;

    Canvas(Editor editor) {
        this.editor = editor;
        createFrame();
        attachKeyboardListeners();
        attachMouseListeners();
        refresh();
    }

    private void createFrame() {
        frame = new JFrame();
        frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        frame.setLocationRelativeTo(null);

        JPanel contentPanel = new JPanel();
        Border padding = BorderFactory.createEmptyBorder(PADDING, PADDING, PADDING, PADDING);
        contentPanel.setBorder(padding);
        contentPanel.setLayout(new BoxLayout(contentPanel, BoxLayout.Y_AXIS));
        frame.setContentPane(contentPanel);

        contentPanel.add(new JLabel("Select and drag to move."), BorderLayout.PAGE_END);
        contentPanel.add(new JLabel("Right click to change color."), BorderLayout.PAGE_END);
        contentPanel.add(new JLabel("Undo: Ctrl+Z, Redo: Ctrl+R"), BorderLayout.PAGE_END);
        contentPanel.add(this);
        frame.setVisible(true);
        contentPanel.setBackground(Color.LIGHT_GRAY);
    }

    private void attachKeyboardListeners() {
        addKeyListener(new KeyAdapter() {
            @Override
            public void keyPressed(KeyEvent e) {
                if ((e.getModifiers() & KeyEvent.CTRL_MASK) != 0) {
                    switch (e.getKeyCode()) {
                        case KeyEvent.VK_Z:
                            editor.undo();
                            break;
                        case KeyEvent.VK_R:
                            editor.redo();
                            break;
                    }
                }
            }
        });
    }

    private void attachMouseListeners() {
        MouseAdapter colorizer = new MouseAdapter() {
            @Override
            public void mousePressed(MouseEvent e) {
                if (e.getButton() != MouseEvent.BUTTON3) {
                    return;
                }
                Shape target = editor.getShapes().getChildAt(e.getX(), e.getY());
                if (target != null) {
                    editor.execute(new ColorCommand(editor, new Color((int) (Math.random() * 0x1000000))));
                    repaint();
                }
            }
        };
        addMouseListener(colorizer);

        MouseAdapter selector = new MouseAdapter() {
            @Override
            public void mousePressed(MouseEvent e) {
                if (e.getButton() != MouseEvent.BUTTON1) {
                    return;
                }

                Shape target = editor.getShapes().getChildAt(e.getX(), e.getY());
                boolean ctrl = (e.getModifiers() & ActionEvent.CTRL_MASK) == ActionEvent.CTRL_MASK;

                if (target == null) {
                    if (!ctrl) {
                        editor.getShapes().unSelect();
                    }
                } else {
                    if (ctrl) {
                        if (target.isSelected()) {
                            target.unSelect();
                        } else {
                            target.select();
                        }
                    } else {
                        if (!target.isSelected()) {
                            editor.getShapes().unSelect();
                        }
                        target.select();
                    }
                }
                repaint();
            }
        };
        addMouseListener(selector);


        MouseAdapter dragger = new MouseAdapter() {
            MoveCommand moveCommand;

            @Override
            public void mouseDragged(MouseEvent e) {
                if ((e.getModifiersEx() & MouseEvent.BUTTON1_DOWN_MASK) != MouseEvent.BUTTON1_DOWN_MASK) {
                    return;
                }
                if (moveCommand == null) {
                    moveCommand = new MoveCommand(editor);
                    moveCommand.start(e.getX(), e.getY());
                }
                moveCommand.move(e.getX(), e.getY());
                repaint();
            }

            @Override
            public void mouseReleased(MouseEvent e) {
                if (e.getButton() != MouseEvent.BUTTON1 || moveCommand == null) {
                    return;
                }
                moveCommand.stop(e.getX(), e.getY());
                editor.execute(moveCommand);
                this.moveCommand = null;
                repaint();
            }
        };
        addMouseListener(dragger);
        addMouseMotionListener(dragger);
    }

    public int getWidth() {
        return editor.getShapes().getX() + editor.getShapes().getWidth() + PADDING;
    }

    public int getHeight() {
        return editor.getShapes().getY() + editor.getShapes().getHeight() + PADDING;
    }

    void refresh() {
        this.setSize(getWidth(), getHeight());
        frame.pack();
    }

    public void update(Graphics g) {
        paint(g);
    }

    public void paint(Graphics graphics) {
        BufferedImage buffer = new BufferedImage(this.getWidth(), this.getHeight(), BufferedImage.TYPE_INT_RGB);
        Graphics2D ig2 = buffer.createGraphics();
        ig2.setBackground(Color.WHITE);
        ig2.clearRect(0, 0, this.getWidth(), this.getHeight());

        editor.getShapes().paint(buffer.getGraphics());

        graphics.drawImage(buffer, 0, 0, null);
    }
}
View Code

  history/History.java: 保存命令和备忘录的历史记录

package memento.history;

import memento.commands.Command;

import java.util.ArrayList;
import java.util.List;

/**
 * @author GaoMing
 * @date 2021/7/25 - 20:47
 */
public class History {
    private List<Pair> history = new ArrayList<Pair>();
    private int virtualSize = 0;

    private class Pair {
        Command command;
        Memento memento;
        Pair(Command c, Memento m) {
            command = c;
            memento = m;
        }

        private Command getCommand() {
            return command;
        }

        private Memento getMemento() {
            return memento;
        }
    }

    public void push(Command c, Memento m) {
        if (virtualSize != history.size() && virtualSize > 0) {
            history = history.subList(0, virtualSize - 1);
        }
        history.add(new Pair(c, m));
        virtualSize = history.size();
    }

    public boolean undo() {
        Pair pair = getUndo();
        if (pair == null) {
            return false;
        }
        System.out.println("Undoing: " + pair.getCommand().getName());
        pair.getMemento().restore();
        return true;
    }

    public boolean redo() {
        Pair pair = getRedo();
        if (pair == null) {
            return false;
        }
        System.out.println("Redoing: " + pair.getCommand().getName());
        pair.getMemento().restore();
        pair.getCommand().execute();
        return true;
    }

    private Pair getUndo() {
        if (virtualSize == 0) {
            return null;
        }
        virtualSize = Math.max(0, virtualSize - 1);
        return history.get(virtualSize);
    }

    private Pair getRedo() {
        if (virtualSize == history.size()) {
            return null;
        }
        virtualSize = Math.min(history.size(), virtualSize + 1);
        return history.get(virtualSize - 1);
    }
}

  history/Memento.java:备忘录类

package memento.history;
import memento.editor.Editor;

/**
 * @author GaoMing
 * @date 2021/7/25 - 20:48
 */
public class Memento {
    private String backup;
    private Editor editor;

    public Memento(Editor editor) {
        this.editor = editor;
        this.backup = editor.backup();
    }

    public void restore() {
        editor.restore(backup);
    }
}

  commands/Command.java: 基础命令类

package memento.commands;

/**
 * @author GaoMing
 * @date 2021/7/25 - 20:52
 */
public interface Command {
    String getName();
    void execute();
}

  commands/ColorCommand.java: 修改已选形状的颜色

package memento.commands;

import memento.editor.Editor;
import memento.shapes.Shape;

import java.awt.*;


/**
 * @author GaoMing
 * @date 2021/7/25 - 20:52
 */
public class ColorCommand implements Command{
    private Editor editor;
    private Color color;

    public ColorCommand(Editor editor, Color color) {
        this.editor = editor;
        this.color = color;
    }

    @Override
    public String getName() {
        return "Colorize: " + color.toString();
    }

    @Override
    public void execute() {
        for (Shape child : editor.getShapes().getSelected()) {
            child.setColor(color);
        }
    }
}

  commands/MoveCommand.java: 移动已选形状

package memento.commands;


import memento.editor.Editor;
import memento.shapes.Shape;

/**
 * @author GaoMing
 * @date 2021/7/25 - 20:53
 */
public class MoveCommand implements Command{
    private Editor editor;
    private int startX, startY;
    private int endX, endY;

    public MoveCommand(Editor editor) {
        this.editor = editor;
    }

    @Override
    public String getName() {
        return "Move by X:" + (endX - startX) + " Y:" + (endY - startY);
    }

    public void start(int x, int y) {
        startX = x;
        startY = y;
        for (Shape child : editor.getShapes().getSelected()) {
            child.drag();
        }
    }

    public void move(int x, int y) {
        for (Shape child : editor.getShapes().getSelected()) {
            child.moveTo(x - startX, y - startY);
        }
    }

    public void stop(int x, int y) {
        endX = x;
        endY = y;
        for (Shape child : editor.getShapes().getSelected()) {
            child.drop();
        }
    }

    @Override
    public void execute() {
        for (Shape child : editor.getShapes().getSelected()) {
            child.moveBy(endX - startX, endY - startY);
        }
    }
}

  shapes/Shape.java

package memento.shapes;

import java.awt.*;
import java.io.Serializable;

/**
 * @author GaoMing
 * @date 2021/7/25 - 20:55
 */
public interface Shape extends Serializable {
    int getX();
    int getY();
    int getWidth();
    int getHeight();
    void drag();
    void drop();
    void moveTo(int x, int y);
    void moveBy(int x, int y);
    boolean isInsideBounds(int x, int y);
    Color getColor();
    void setColor(Color color);
    void select();
    void unSelect();
    boolean isSelected();
    void paint(Graphics graphics);
}

  shapes/BaseShape.java

package memento.shapes;

import java.awt.*;

/**
 * @author GaoMing
 * @date 2021/7/25 - 20:56
 */
public abstract class BaseShape implements Shape{
    int x, y;
    private int dx = 0, dy = 0;
    private Color color;
    private boolean selected = false;

    BaseShape(int x, int y, Color color) {
        this.x = x;
        this.y = y;
        this.color = color;
    }

    @Override
    public int getX() {
        return x;
    }

    @Override
    public int getY() {
        return y;
    }

    @Override
    public int getWidth() {
        return 0;
    }

    @Override
    public int getHeight() {
        return 0;
    }

    @Override
    public void drag() {
        dx = x;
        dy = y;
    }

    @Override
    public void moveTo(int x, int y) {
        this.x = dx + x;
        this.y = dy + y;
    }

    @Override
    public void moveBy(int x, int y) {
        this.x += x;
        this.y += y;
    }

    @Override
    public void drop() {
        this.x = dx;
        this.y = dy;
    }

    @Override
    public boolean isInsideBounds(int x, int y) {
        return x > getX() && x < (getX() + getWidth()) &&
                y > getY() && y < (getY() + getHeight());
    }

    @Override
    public Color getColor() {
        return color;
    }

    @Override
    public void setColor(Color color) {
        this.color = color;
    }

    @Override
    public void select() {
        selected = true;
    }

    @Override
    public void unSelect() {
        selected = false;
    }

    @Override
    public boolean isSelected() {
        return selected;
    }

    void enableSelectionStyle(Graphics graphics) {
        graphics.setColor(Color.LIGHT_GRAY);

        Graphics2D g2 = (Graphics2D) graphics;
        float dash1[] = {2.0f};
        g2.setStroke(new BasicStroke(1.0f,
                BasicStroke.CAP_BUTT,
                BasicStroke.JOIN_MITER,
                2.0f, dash1, 0.0f));
    }

    void disableSelectionStyle(Graphics graphics) {
        graphics.setColor(color);
        Graphics2D g2 = (Graphics2D) graphics;
        g2.setStroke(new BasicStroke());
    }

    @Override
    public void paint(Graphics graphics) {
        if (isSelected()) {
            enableSelectionStyle(graphics);
        }
        else {
            disableSelectionStyle(graphics);
        }

        // ...
    }
}
View Code

  shapes/Circle.java

package memento.shapes;

import java.awt.*;

/**
 * @author GaoMing
 * @date 2021/7/25 - 20:57
 */
public class Circle extends BaseShape{
    private int radius;

    public Circle(int x, int y, int radius, Color color) {
        super(x, y, color);
        this.radius = radius;
    }

    @Override
    public int getWidth() {
        return radius * 2;
    }

    @Override
    public int getHeight() {
        return radius * 2;
    }

    @Override
    public void paint(Graphics graphics) {
        super.paint(graphics);
        graphics.drawOval(x, y, getWidth() - 1, getHeight() - 1);
    }
}

  shapes/Dot.java

package memento.shapes;

import java.awt.*;

/**
 * @author GaoMing
 * @date 2021/7/25 - 20:57
 */
public class Dot extends BaseShape{
    private final int DOT_SIZE = 3;

    public Dot(int x, int y, Color color) {
        super(x, y, color);
    }

    @Override
    public int getWidth() {
        return DOT_SIZE;
    }

    @Override
    public int getHeight() {
        return DOT_SIZE;
    }

    @Override
    public void paint(Graphics graphics) {
        super.paint(graphics);
        graphics.fillRect(x - 1, y - 1, getWidth(), getHeight());
    }
}

  shapes/Rectangle.java

package memento.shapes;

import java.awt.*;

/**
 * @author GaoMing
 * @date 2021/7/25 - 20:58
 */
public class Rectangle extends BaseShape{
    private int width;
    private int height;

    public Rectangle(int x, int y, int width, int height, Color color) {
        super(x, y, color);
        this.width = width;
        this.height = height;
    }

    @Override
    public int getWidth() {
        return width;
    }

    @Override
    public int getHeight() {
        return height;
    }

    @Override
    public void paint(Graphics graphics) {
        super.paint(graphics);
        graphics.drawRect(x, y, getWidth() - 1, getHeight() - 1);
    }
}

  shapes/CompoundShape.java

package memento.shapes;

import java.awt.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * @author GaoMing
 * @date 2021/7/25 - 20:59
 */
public class CompoundShape extends BaseShape{
    private List<Shape> children = new ArrayList<>();

    public CompoundShape(Shape... components) {
        super(0, 0, Color.BLACK);
        add(components);
    }

    public void add(Shape component) {
        children.add(component);
    }

    public void add(Shape... components) {
        children.addAll(Arrays.asList(components));
    }

    public void remove(Shape child) {
        children.remove(child);
    }

    public void remove(Shape... components) {
        children.removeAll(Arrays.asList(components));
    }

    public void clear() {
        children.clear();
    }

    @Override
    public int getX() {
        if (children.size() == 0) {
            return 0;
        }
        int x = children.get(0).getX();
        for (Shape child : children) {
            if (child.getX() < x) {
                x = child.getX();
            }
        }
        return x;
    }

    @Override
    public int getY() {
        if (children.size() == 0) {
            return 0;
        }
        int y = children.get(0).getY();
        for (Shape child : children) {
            if (child.getY() < y) {
                y = child.getY();
            }
        }
        return y;
    }

    @Override
    public int getWidth() {
        int maxWidth = 0;
        int x = getX();
        for (Shape child : children) {
            int childsRelativeX = child.getX() - x;
            int childWidth = childsRelativeX + child.getWidth();
            if (childWidth > maxWidth) {
                maxWidth = childWidth;
            }
        }
        return maxWidth;
    }

    @Override
    public int getHeight() {
        int maxHeight = 0;
        int y = getY();
        for (Shape child : children) {
            int childsRelativeY = child.getY() - y;
            int childHeight = childsRelativeY + child.getHeight();
            if (childHeight > maxHeight) {
                maxHeight = childHeight;
            }
        }
        return maxHeight;
    }

    @Override
    public void drag() {
        for (Shape child : children) {
            child.drag();
        }
    }

    @Override
    public void drop() {
        for (Shape child : children) {
            child.drop();
        }
    }

    @Override
    public void moveTo(int x, int y) {
        for (Shape child : children) {
            child.moveTo(x, y);
        }
    }

    @Override
    public void moveBy(int x, int y) {
        for (Shape child : children) {
            child.moveBy(x, y);
        }
    }

    @Override
    public boolean isInsideBounds(int x, int y) {
        for (Shape child : children) {
            if (child.isInsideBounds(x, y)) {
                return true;
            }
        }
        return false;
    }

    @Override
    public void setColor(Color color) {
        super.setColor(color);
        for (Shape child : children) {
            child.setColor(color);
        }
    }

    @Override
    public void unSelect() {
        super.unSelect();
        for (Shape child : children) {
            child.unSelect();
        }
    }

    public Shape getChildAt(int x, int y) {
        for (Shape child : children) {
            if (child.isInsideBounds(x, y)) {
                return child;
            }
        }
        return null;
    }

    public boolean selectChildAt(int x, int y) {
        Shape child = getChildAt(x,y);
        if (child != null) {
            child.select();
            return true;
        }
        return false;
    }

    public List<Shape> getSelected() {
        List<Shape> selected = new ArrayList<>();
        for (Shape child : children) {
            if (child.isSelected()) {
                selected.add(child);
            }
        }
        return selected;
    }

    @Override
    public void paint(Graphics graphics) {
        if (isSelected()) {
            enableSelectionStyle(graphics);
            graphics.drawRect(getX() - 1, getY() - 1, getWidth() + 1, getHeight() + 1);
            disableSelectionStyle(graphics);
        }

        for (Shape child : children) {
            child.paint(graphics);
        }
    }

}
View Code

  Demo.java: 初始化代码

package memento;

import memento.editor.Editor;
import memento.shapes.Circle;
import memento.shapes.CompoundShape;
import memento.shapes.Dot;
import memento.shapes.Rectangle;

import java.awt.*;

/**
 * @author GaoMing
 * @date 2021/7/25 - 20:47
 */
public class Demo {
    public static void main(String[] args) {
        Editor editor = new Editor();
        editor.loadShapes(
                new Circle(10, 10, 10, Color.BLUE),

                new CompoundShape(
                        new Circle(110, 110, 50, Color.RED),
                        new Dot(160, 160, Color.RED)
                ),

                new CompoundShape(
                        new Rectangle(250, 250, 100, 100, Color.GREEN),
                        new Dot(240, 240, Color.GREEN),
                        new Dot(240, 360, Color.GREEN),
                        new Dot(360, 360, Color.GREEN),
                        new Dot(360, 240, Color.GREEN)
                )
        );
    }
}

  运行结果    

 

  7. 与其他模式的关系

  • 可以同时使用命令模式和备忘录模式来实现 “撤销”。在这种情况下,命令用于对目标对象执行各种不同的操作,备忘录用来保存一条命令执行前该对象的状态
  • 可以同时使用备忘录和迭代器模式来获取当前迭代器的状态,并且在需要的时候进行回滚
  • 有时候原型模式可以作为备忘录的一个简化版本,其条件是你需要在历史记录中存储的对象的状态比较简单,不需要链接其他外部资源,或者链接可以方便地重建

  8. 已知应用

  使用示例:备忘录的基本原则可通过序列化来实现,这在Java语言中很常见。尽管备忘录不是生成对象状态快照的唯一或最有效方法,但它能在保护原始对象的结构不暴露给其他对象的情况下保存对象状态的备份
  下面是核心 Java 程序库中该模式的一些示例:

  所有 java.io.Serializable 的实现都可以模拟备忘录
  所有 javax.faces.component.StateHolder 的实现

posted @ 2021-07-28 12:56  慕仙白  阅读(180)  评论(0编辑  收藏  举报